From 9ffc65098379a5d0b3b8627debd3be5b63e7c1c5 Mon Sep 17 00:00:00 2001 From: Olivier FAURE Date: Mon, 20 Jan 2025 12:52:08 +0000 Subject: [PATCH] Remove `Box` from a few places (#837) Having `Box` implement the `Widget` trait is a crutch that makes a bunch of things more complicated. It leads to double-boxing in cases when the `dyn Widget` is itself a `Box` (especially since the arena's current implementation boxes all widgets by default), makes it harder to reason about downcasting, and ends up producing a lot of code to handle the edge cases. On the xilem side, `Box` is slightly redundant with `DynWidget`. This PR still leaves a lot of boxing. On the long term, we'd like for the arena to pack arbitrary widgets efficiently using some kind of archetype scheme, but in the meantime, boxing will have to do. Changes in this PR: - New `FromDynWidget` trait that maybe-downcasts widgets to a maybe-sized `Widget` type. - Most places that accept `Widget` now accept `Widget + ?Sized`. - Remove `impl Widget for Box`. - Replace all instances of `WidgetPod>` with `WidgetPod`. - Replace all instances of `xilem::Pod>` with `xilem::Pod`. - Rename WidgetPod to WidgetBox in xilem_core example to avoid ambiguity. --- masonry/src/contexts.rs | 96 +++++++------ masonry/src/doc/01_creating_app.md | 2 +- .../doc/03_implementing_container_widget.md | 4 +- masonry/src/lib.rs | 2 +- masonry/src/passes/accessibility.rs | 2 +- masonry/src/passes/event.rs | 2 +- masonry/src/passes/layout.rs | 2 +- masonry/src/passes/mutate.rs | 4 +- masonry/src/passes/update.rs | 4 +- masonry/src/render_root.rs | 47 ++---- masonry/src/testing/harness.rs | 7 +- masonry/src/testing/helper_widgets.rs | 18 +-- masonry/src/widget/align.rs | 8 +- masonry/src/widget/flex.rs | 36 ++--- masonry/src/widget/grid.rs | 30 ++-- masonry/src/widget/portal.rs | 15 +- masonry/src/widget/root_widget.rs | 12 +- masonry/src/widget/sized_box.rs | 14 +- masonry/src/widget/split.rs | 8 +- masonry/src/widget/tests/transforms.rs | 18 +-- masonry/src/widget/widget.rs | 134 +++++------------- masonry/src/widget/widget_mut.rs | 20 +-- masonry/src/widget/widget_pod.rs | 37 +++-- masonry/src/widget/zstack.rs | 18 +-- xilem/src/any_view.rs | 21 ++- xilem/src/lib.rs | 56 ++++---- xilem/src/one_of.rs | 54 ++++--- xilem/src/view/flex.rs | 18 +-- xilem/src/view/grid.rs | 16 +-- xilem/src/view/sized_box.rs | 2 +- xilem/src/view/zstack.rs | 18 +-- xilem_core/examples/user_interface.rs | 20 +-- 32 files changed, 335 insertions(+), 410 deletions(-) diff --git a/masonry/src/contexts.rs b/masonry/src/contexts.rs index 3d9dbcfc..0ee2a067 100644 --- a/masonry/src/contexts.rs +++ b/masonry/src/contexts.rs @@ -17,8 +17,8 @@ use crate::text::BrushIndex; use crate::theme::get_debug_color; use crate::widget::{CreateWidget, WidgetMut, WidgetRef, WidgetState}; use crate::{ - Affine, AllowRawMut, BoxConstraints, Color, Insets, Point, Rect, Size, Vec2, Widget, WidgetId, - WidgetPod, + Affine, AllowRawMut, BoxConstraints, Color, FromDynWidget, Insets, Point, Rect, Size, Vec2, + Widget, WidgetId, WidgetPod, }; // Note - Most methods defined in this file revolve around `WidgetState` fields. @@ -154,9 +154,19 @@ impl_context_method!( child_ref.item.as_dyn_any().downcast_ref::().unwrap() } + #[allow(dead_code, reason = "Copy-pasted for some types that don't need it")] + /// Helper method to get a direct reference to a child widget from its `WidgetPod`. + fn get_child_dyn(&self, child: &'_ WidgetPod) -> &'_ dyn Widget { + let child_ref = self + .widget_children + .get_child(child.id()) + .expect("get_child: child not found"); + child_ref.item.as_dyn() + } + #[allow(dead_code, reason = "Copy-pasted for some types that don't need it")] /// Helper method to get a direct reference to a child widget's `WidgetState` from its `WidgetPod`. - fn get_child_state(&self, child: &'_ WidgetPod) -> &'_ WidgetState { + fn get_child_state(&self, child: &'_ WidgetPod) -> &'_ WidgetState { let child_state_ref = self .widget_state_children .get_child(child.id()) @@ -182,7 +192,7 @@ impl_context_method!( /// /// This one isn't defined for `PaintCtx` and `AccessCtx` because those contexts /// can't mutate `WidgetState`. - fn get_child_state_mut( + fn get_child_state_mut( &mut self, child: &'_ mut WidgetPod, ) -> &'_ mut WidgetState { @@ -199,7 +209,7 @@ impl_context_method!( // Methods to get a child WidgetMut from a parent. impl MutateCtx<'_> { /// Return a [`WidgetMut`] to a child widget. - pub fn get_mut<'c, Child: Widget>( + pub fn get_mut<'c, Child: Widget + FromDynWidget + ?Sized>( &'c mut self, child: &'c mut WidgetPod, ) -> WidgetMut<'c, Child> { @@ -220,7 +230,7 @@ impl MutateCtx<'_> { }; WidgetMut { ctx: child_ctx, - widget: child_mut.item.as_mut_dyn_any().downcast_mut().unwrap(), + widget: Child::from_dyn_mut(&mut **child_mut.item).unwrap(), } } @@ -270,7 +280,7 @@ impl<'w> QueryCtx<'w> { WidgetRef { ctx, - widget: child.item, + widget: &**child.item, } } } @@ -433,26 +443,26 @@ impl EventCtx<'_> { // --- MARK: UPDATE LAYOUT --- impl LayoutCtx<'_> { #[track_caller] - fn assert_layout_done(&self, child: &WidgetPod, method_name: &str) { + fn assert_layout_done(&self, child: &WidgetPod, method_name: &str) { if self.get_child_state(child).needs_layout { debug_panic!( "Error in {}: trying to call '{}' with child '{}' {} before computing its layout", self.widget_id(), method_name, - self.get_child(child).short_type_name(), + self.get_child_dyn(child).short_type_name(), child.id(), ); } } #[track_caller] - fn assert_placed(&self, child: &WidgetPod, method_name: &str) { + fn assert_placed(&self, child: &WidgetPod, method_name: &str) { if self.get_child_state(child).is_expecting_place_child_call { debug_panic!( "Error in {}: trying to call '{}' with child '{}' {} before placing it", self.widget_id(), method_name, - self.get_child(child).short_type_name(), + self.get_child_dyn(child).short_type_name(), child.id(), ); } @@ -464,7 +474,11 @@ impl LayoutCtx<'_> { /// their [`layout`] method. /// /// [`layout`]: Widget::layout - pub fn run_layout(&mut self, child: &mut WidgetPod, bc: &BoxConstraints) -> Size { + pub fn run_layout( + &mut self, + child: &mut WidgetPod, + bc: &BoxConstraints, + ) -> Size { run_layout_on(self, child, bc) } @@ -479,7 +493,7 @@ impl LayoutCtx<'_> { /// This method will panic if [`LayoutCtx::run_layout`] has not been called yet for /// the child. #[track_caller] - pub fn place_child(&mut self, child: &mut WidgetPod, origin: Point) { + pub fn place_child(&mut self, child: &mut WidgetPod, origin: Point) { self.assert_layout_done(child, "place_child"); if origin.x.is_nan() || origin.x.is_infinite() @@ -489,7 +503,7 @@ impl LayoutCtx<'_> { debug_panic!( "Error in {}: trying to call 'place_child' with child '{}' {} with invalid origin {:?}", self.widget_id(), - self.get_child(child).short_type_name(), + self.get_child_dyn(child).short_type_name(), child.id(), origin, ); @@ -533,7 +547,7 @@ impl LayoutCtx<'_> { #[track_caller] pub fn compute_insets_from_child( &mut self, - child: &WidgetPod, + child: &WidgetPod, my_size: Size, ) -> Insets { self.assert_layout_done(child, "compute_insets_from_child"); @@ -565,7 +579,7 @@ impl LayoutCtx<'_> { } /// Returns whether a child of this widget needs to call [`LayoutCtx::run_layout`]. - pub fn child_needs_layout(&self, child: &WidgetPod) -> bool { + pub fn child_needs_layout(&self, child: &WidgetPod) -> bool { self.get_child_state(child).needs_layout } @@ -576,7 +590,7 @@ impl LayoutCtx<'_> { /// This method will panic if [`LayoutCtx::run_layout`] has not been called yet for /// the child. #[track_caller] - pub fn child_baseline_offset(&self, child: &WidgetPod) -> f64 { + pub fn child_baseline_offset(&self, child: &WidgetPod) -> f64 { self.assert_layout_done(child, "child_baseline_offset"); self.get_child_state(child).baseline_offset } @@ -588,7 +602,7 @@ impl LayoutCtx<'_> { /// This method will panic if [`LayoutCtx::run_layout`] and [`LayoutCtx::place_child`] /// have not been called yet for the child. #[track_caller] - pub fn child_layout_rect(&self, child: &WidgetPod) -> Rect { + pub fn child_layout_rect(&self, child: &WidgetPod) -> Rect { self.assert_layout_done(child, "child_layout_rect"); self.assert_placed(child, "child_layout_rect"); self.get_child_state(child).layout_rect() @@ -601,7 +615,7 @@ impl LayoutCtx<'_> { /// This method will panic if [`LayoutCtx::run_layout`] and [`LayoutCtx::place_child`] /// have not been called yet for the child. #[track_caller] - pub fn child_paint_rect(&self, child: &WidgetPod) -> Rect { + pub fn child_paint_rect(&self, child: &WidgetPod) -> Rect { self.assert_layout_done(child, "child_paint_rect"); self.assert_placed(child, "child_paint_rect"); self.get_child_state(child).paint_rect() @@ -614,7 +628,7 @@ impl LayoutCtx<'_> { /// This method will panic if [`LayoutCtx::run_layout`] has not been called yet for /// the child. #[track_caller] - pub fn child_size(&self, child: &WidgetPod) -> Size { + pub fn child_size(&self, child: &WidgetPod) -> Size { self.assert_layout_done(child, "child_size"); self.get_child_state(child).layout_rect().size() } @@ -623,7 +637,7 @@ impl LayoutCtx<'_> { /// /// This may be removed in the future. Currently it's useful for /// stashed children and children whose layout is cached. - pub fn skip_layout(&mut self, child: &mut WidgetPod) { + pub fn skip_layout(&mut self, child: &mut WidgetPod) { self.get_child_state_mut(child).request_layout = false; } @@ -669,9 +683,9 @@ impl ComposeCtx<'_> { /// Set the scroll translation for the child widget. /// /// The translation is applied on top of the position from [`LayoutCtx::place_child`]. - pub fn set_child_scroll_translation( + pub fn set_child_scroll_translation( &mut self, - child: &mut WidgetPod, + child: &mut WidgetPod, translation: Vec2, ) { if translation.x.is_nan() @@ -682,7 +696,7 @@ impl ComposeCtx<'_> { debug_panic!( "Error in {}: trying to call 'set_child_scroll_translation' with child '{}' {} with invalid translation {:?}", self.widget_id(), - self.get_child(child).short_type_name(), + self.get_child_dyn(child).short_type_name(), child.id(), translation, ); @@ -974,7 +988,7 @@ impl_context_method!(MutateCtx<'_>, EventCtx<'_>, UpdateCtx<'_>, { /// /// Container widgets should avoid dropping `WidgetPod`s. Instead, they should /// pass them to this method. - pub fn remove_child(&mut self, child: WidgetPod) { + pub fn remove_child(&mut self, child: WidgetPod) { // TODO - Send recursive event to child let id = child.id(); let _ = self @@ -1025,7 +1039,7 @@ impl_context_method!( /// If `stashed` is true, the child will not be painted or listed in the accessibility tree. /// /// This will *not* trigger a layout pass. - pub fn set_stashed(&mut self, child: &mut WidgetPod, stashed: bool) { + pub fn set_stashed(&mut self, child: &mut WidgetPod, stashed: bool) { let child_state = self.get_child_state_mut(child); // Stashing is generally a property derived from the parent widget's state // (rather than set imperatively), so it is likely to be set as part of passes. @@ -1042,7 +1056,7 @@ impl_context_method!( /// The callbacks will be run in the order they were submitted during the mutate pass. pub fn mutate_self_later( &mut self, - f: impl FnOnce(WidgetMut<'_, Box>) + Send + 'static, + f: impl FnOnce(WidgetMut<'_, dyn Widget>) + Send + 'static, ) { let callback = MutateCallback { id: self.widget_state.id, @@ -1054,7 +1068,7 @@ impl_context_method!( /// Queue a callback that will be called with a [`WidgetMut`] for the given child widget. /// /// The callbacks will be run in the order they were submitted during the mutate pass. - pub fn mutate_later( + pub fn mutate_later( &mut self, child: &mut WidgetPod, f: impl FnOnce(WidgetMut<'_, W>) + Send + 'static, @@ -1155,7 +1169,7 @@ impl RegisterCtx<'_> { /// /// Container widgets should call this on all their children in /// their implementation of [`Widget::register_children`]. - pub fn register_child(&mut self, child: &mut WidgetPod) { + pub fn register_child(&mut self, child: &mut WidgetPod) { let Some(CreateWidget { widget, transform }) = child.take_inner() else { return; }; @@ -1168,7 +1182,7 @@ impl RegisterCtx<'_> { let id = child.id(); let state = WidgetState::new(child.id(), widget.short_type_name(), transform); - self.widget_children.insert_child(id, Box::new(widget)); + self.widget_children.insert_child(id, widget.as_box_dyn()); self.widget_state_children.insert_child(id, state); } } @@ -1205,7 +1219,7 @@ macro_rules! impl_get_raw { /// /// The child context can be used to call context methods on behalf of the /// child widget. - pub fn get_raw_ref<'a, 'r, Child: Widget>( + pub fn get_raw_ref<'a, 'r, Child: Widget + FromDynWidget + ?Sized>( &'a mut self, child: &'a mut WidgetPod, ) -> RawWrapper<'r, $SomeCtx<'r>, Child> @@ -1231,14 +1245,14 @@ macro_rules! impl_get_raw { }; RawWrapper { ctx: child_ctx, - widget: child_mut.item.as_dyn_any().downcast_ref().unwrap(), + widget: Child::from_dyn(&**child_mut.item).unwrap(), } } /// Get a raw mutable reference to a child widget. /// /// See documentation for [`AllowRawMut`] for more details. - pub fn get_raw_mut<'a, 'r, Child: Widget + AllowRawMut>( + pub fn get_raw_mut<'a, 'r, Child: Widget + FromDynWidget + AllowRawMut + ?Sized>( &'a mut self, child: &'a mut WidgetPod, ) -> RawWrapperMut<'r, $SomeCtx<'r>, Child> @@ -1265,7 +1279,7 @@ macro_rules! impl_get_raw { RawWrapperMut { parent_widget_state: &mut self.widget_state, ctx: child_ctx, - widget: child_mut.item.as_mut_dyn_any().downcast_mut().unwrap(), + widget: Child::from_dyn_mut(&mut **child_mut.item).unwrap(), } } } @@ -1278,7 +1292,7 @@ impl_get_raw!(LayoutCtx); #[allow(missing_docs, reason = "RawWrapper is likely to be reworked")] impl<'s> AccessCtx<'s> { - pub fn get_raw_ref<'a, 'r, Child: Widget>( + pub fn get_raw_ref<'a, 'r, Child: Widget + FromDynWidget + ?Sized>( &'a mut self, child: &'a WidgetPod, ) -> RawWrapper<'r, AccessCtx<'r>, Child> @@ -1304,26 +1318,26 @@ impl<'s> AccessCtx<'s> { }; RawWrapper { ctx: child_ctx, - widget: child_mut.item.as_dyn_any().downcast_ref().unwrap(), + widget: Child::from_dyn(&**child_mut.item).unwrap(), } } } #[allow(missing_docs, reason = "RawWrapper is likely to be reworked")] -pub struct RawWrapper<'a, Ctx, W> { +pub struct RawWrapper<'a, Ctx, W: ?Sized> { ctx: Ctx, widget: &'a W, } #[allow(missing_docs, reason = "RawWrapper is likely to be reworked")] -pub struct RawWrapperMut<'a, Ctx: IsContext, W> { +pub struct RawWrapperMut<'a, Ctx: IsContext, W: ?Sized> { parent_widget_state: &'a mut WidgetState, ctx: Ctx, widget: &'a mut W, } #[allow(missing_docs, reason = "RawWrapper is likely to be reworked")] -impl RawWrapper<'_, Ctx, W> { +impl RawWrapper<'_, Ctx, W> { pub fn widget(&self) -> &W { self.widget } @@ -1334,7 +1348,7 @@ impl RawWrapper<'_, Ctx, W> { } #[allow(missing_docs, reason = "RawWrapper is likely to be reworked")] -impl RawWrapperMut<'_, Ctx, W> { +impl RawWrapperMut<'_, Ctx, W> { pub fn widget(&mut self) -> &mut W { self.widget } @@ -1344,7 +1358,7 @@ impl RawWrapperMut<'_, Ctx, W> { } } -impl Drop for RawWrapperMut<'_, Ctx, W> { +impl Drop for RawWrapperMut<'_, Ctx, W> { fn drop(&mut self) { self.parent_widget_state .merge_up(self.ctx.get_widget_state()); diff --git a/masonry/src/doc/01_creating_app.md b/masonry/src/doc/01_creating_app.md index dd11938f..4ab22687 100644 --- a/masonry/src/doc/01_creating_app.md +++ b/masonry/src/doc/01_creating_app.md @@ -116,7 +116,7 @@ Because our widget tree only has one button and one textbox, there is no possibl When handling `ButtonPressed`: - `ctx.render_root()` returns a reference to the `RenderRoot`, which owns the widget tree and all the associated visual state. -- `RenderRoot::edit_root_widget()` takes a closure; that closure takes a `WidgetMut>` which we call `root`. Once the closure returns, `RenderRoot` runs some passes to update the app's internal states. +- `RenderRoot::edit_root_widget()` takes a closure; that closure takes a `WidgetMut` which we call `root`. Once the closure returns, `RenderRoot` runs some passes to update the app's internal states. - `root.downcast::<...>()` returns a `WidgetMut>`. - `RootWidget::child_mut()` returns a `WidgetMut>`. - `Portal::child_mut()` returns a `WidgetMut`. diff --git a/masonry/src/doc/03_implementing_container_widget.md b/masonry/src/doc/03_implementing_container_widget.md index 48d7e358..767600ed 100644 --- a/masonry/src/doc/03_implementing_container_widget.md +++ b/masonry/src/doc/03_implementing_container_widget.md @@ -28,7 +28,7 @@ As an example, let's write a `VerticalStack` widget, which lays out its children ```rust,ignore struct VerticalStack { - children: Vec>>, + children: Vec>, gap: f64, } @@ -199,7 +199,7 @@ Let's write WidgetMut methods for our `VerticalStack`: ```rust,ignore impl VerticalStack { - pub fn add_child(this: &mut WidgetMut<'_, Self>, child: WidgetPod>) { + pub fn add_child(this: &mut WidgetMut<'_, Self>, child: WidgetPod) { this.widget.children.push(child); this.ctx.children_changed(); } diff --git a/masonry/src/lib.rs b/masonry/src/lib.rs index 0cf807ca..2aa4a572 100644 --- a/masonry/src/lib.rs +++ b/masonry/src/lib.rs @@ -193,7 +193,7 @@ pub use event::{ pub use paint_scene_helpers::UnitPoint; pub use render_root::{RenderRoot, RenderRootOptions, RenderRootSignal, WindowSizePolicy}; pub use util::{AsAny, Handled}; -pub use widget::widget::{AllowRawMut, Widget, WidgetId}; +pub use widget::widget::{AllowRawMut, FromDynWidget, Widget, WidgetId}; pub use widget::WidgetPod; pub(crate) use widget::WidgetState; diff --git a/masonry/src/passes/accessibility.rs b/masonry/src/passes/accessibility.rs index 306fde15..0843723d 100644 --- a/masonry/src/passes/accessibility.rs +++ b/masonry/src/passes/accessibility.rs @@ -50,7 +50,7 @@ fn build_accessibility_tree( tree_update, rebuild_all, }; - let mut node = build_access_node(widget.item, &mut ctx, scale_factor); + let mut node = build_access_node(&mut **widget.item, &mut ctx, scale_factor); widget.item.accessibility(&mut ctx, &mut node); let id: NodeId = ctx.widget_state.id.into(); diff --git a/masonry/src/passes/event.rs b/masonry/src/passes/event.rs index 77412f5d..56dc90da 100644 --- a/masonry/src/passes/event.rs +++ b/masonry/src/passes/event.rs @@ -75,7 +75,7 @@ fn run_event_pass( ); } - pass_fn(widget, &mut ctx, event); + pass_fn(&mut **widget, &mut ctx, event); is_handled = ctx.is_handled; } diff --git a/masonry/src/passes/layout.rs b/masonry/src/passes/layout.rs index eed18671..2172f87b 100644 --- a/masonry/src/passes/layout.rs +++ b/masonry/src/passes/layout.rs @@ -18,7 +18,7 @@ use crate::{BoxConstraints, LayoutCtx, Widget, WidgetPod}; // --- MARK: RUN LAYOUT --- /// Run [`Widget::layout`] method on the widget contained in `pod`. /// This will be called by [`LayoutCtx::run_layout`], which is itself called in the parent widget's `layout`. -pub(crate) fn run_layout_on( +pub(crate) fn run_layout_on( parent_ctx: &mut LayoutCtx<'_>, pod: &mut WidgetPod, bc: &BoxConstraints, diff --git a/masonry/src/passes/mutate.rs b/masonry/src/passes/mutate.rs index da655796..9fc8fc4b 100644 --- a/masonry/src/passes/mutate.rs +++ b/masonry/src/passes/mutate.rs @@ -11,7 +11,7 @@ use crate::{MutateCtx, Widget, WidgetId}; pub(crate) fn mutate_widget( root: &mut RenderRoot, id: WidgetId, - mutate_fn: impl FnOnce(WidgetMut<'_, Box>) -> R, + mutate_fn: impl FnOnce(WidgetMut<'_, dyn Widget>) -> R, ) -> R { let (widget_mut, state_mut) = root.widget_arena.get_pair_mut(id); @@ -26,7 +26,7 @@ pub(crate) fn mutate_widget( widget_state_children: state_mut.children, widget_children: widget_mut.children, }, - widget: widget_mut.item, + widget: &mut **widget_mut.item, }; let result = mutate_fn(root_widget); diff --git a/masonry/src/passes/update.rs b/masonry/src/passes/update.rs index ef5c9073..f4138594 100644 --- a/masonry/src/passes/update.rs +++ b/masonry/src/passes/update.rs @@ -48,7 +48,7 @@ fn run_targeted_update_pass( widget_state_children: state_mut.children, widget_children: widget_mut.children, }; - pass_fn(widget_mut.item, &mut ctx); + pass_fn(&mut **widget_mut.item, &mut ctx); merge_state_up(&mut root.widget_arena, widget_id); current_id = parent_id; @@ -75,7 +75,7 @@ fn run_single_update_pass( widget_state_children: state_mut.children, widget_children: widget_mut.children, }; - pass_fn(widget_mut.item, &mut ctx); + pass_fn(&mut **widget_mut.item, &mut ctx); let mut current_id = Some(target); while let Some(widget_id) = current_id { diff --git a/masonry/src/render_root.rs b/masonry/src/render_root.rs index 83e8ef89..11ae4793 100644 --- a/masonry/src/render_root.rs +++ b/masonry/src/render_root.rs @@ -52,7 +52,7 @@ const INVALID_IME_AREA: Rect = Rect::new(f64::NAN, f64::NAN, f64::NAN, f64::NAN) /// This is also the type that owns the widget tree. pub struct RenderRoot { /// Root of the widget tree. - pub(crate) root: WidgetPod>, + pub(crate) root: WidgetPod, /// Whether the window size should be determined by the content or the user. pub(crate) size_policy: WindowSizePolicy, @@ -147,7 +147,7 @@ pub(crate) struct RenderRootState { pub(crate) struct MutateCallback { pub(crate) id: WidgetId, - pub(crate) callback: Box>)>, + pub(crate) callback: Box)>, } /// Defines how a windows size should be determined @@ -251,7 +251,7 @@ impl RenderRoot { let debug_paint = std::env::var("MASONRY_DEBUG_PAINT").is_ok_and(|it| !it.is_empty()); let mut root = Self { - root: WidgetPod::new(root_widget).boxed(), + root: WidgetPod::new(root_widget).erased(), size_policy, size: PhysicalSize::new(0, 0), scale_factor, @@ -488,22 +488,13 @@ impl RenderRoot { .into_child(self.root.id()) .expect("root widget not in widget tree"); - // Our WidgetArena stores all widgets as Box, but the "true" - // type of our root widget is *also* Box. We downcast so we - // don't add one more level of indirection to this. - let widget = widget_ref - .item - .as_dyn_any() - .downcast_ref::>() - .unwrap(); - + let widget = &**widget_ref.item; let ctx = QueryCtx { global_state: &self.global_state, widget_state_children: state_ref.children, widget_children: widget_ref.children, widget_state: state_ref.item, }; - WidgetRef { ctx, widget } } @@ -516,12 +507,7 @@ impl RenderRoot { .find(id) .expect("found state but not widget"); - // Box -> &dyn Widget - // Without this step, the type of `WidgetRef::widget` would be - // `&Box as &dyn Widget`, which would be an additional layer - // of indirection. - let widget = widget_ref.item; - let widget: &dyn Widget = &**widget; + let widget = &**widget_ref.item; let ctx = QueryCtx { global_state: &self.global_state, widget_state_children: state_ref.children, @@ -534,25 +520,8 @@ impl RenderRoot { /// Get a [`WidgetMut`] to the root widget. /// /// Because of how `WidgetMut` works, it can only be passed to a user-provided callback. - pub fn edit_root_widget( - &mut self, - f: impl FnOnce(WidgetMut<'_, Box>) -> R, - ) -> R { - let res = mutate_widget(self, self.root.id(), |mut widget_mut| { - // Our WidgetArena stores all widgets as Box, but the "true" - // type of our root widget is *also* Box. We downcast so we - // don't add one more level of indirection to this. - let widget = widget_mut - .widget - .as_mut_dyn_any() - .downcast_mut::>() - .unwrap(); - let widget_mut = WidgetMut { - ctx: widget_mut.ctx.reborrow_mut(), - widget, - }; - f(widget_mut) - }); + pub fn edit_root_widget(&mut self, f: impl FnOnce(WidgetMut<'_, dyn Widget>) -> R) -> R { + let res = mutate_widget(self, self.root.id(), f); self.run_rewrite_passes(); @@ -565,7 +534,7 @@ impl RenderRoot { pub fn edit_widget( &mut self, id: WidgetId, - f: impl FnOnce(WidgetMut<'_, Box>) -> R, + f: impl FnOnce(WidgetMut<'_, dyn Widget>) -> R, ) -> R { let res = mutate_widget(self, id, f); diff --git a/masonry/src/testing/harness.rs b/masonry/src/testing/harness.rs index 5e9238d7..77008022 100644 --- a/masonry/src/testing/harness.rs +++ b/masonry/src/testing/harness.rs @@ -593,10 +593,7 @@ impl TestHarness { /// Get a [`WidgetMut`] to the root widget. /// /// Because of how `WidgetMut` works, it can only be passed to a user-provided callback. - pub fn edit_root_widget( - &mut self, - f: impl FnOnce(WidgetMut<'_, Box>) -> R, - ) -> R { + pub fn edit_root_widget(&mut self, f: impl FnOnce(WidgetMut<'_, dyn Widget>) -> R) -> R { self.render_root.edit_root_widget(f) } @@ -606,7 +603,7 @@ impl TestHarness { pub fn edit_widget( &mut self, id: WidgetId, - f: impl FnOnce(WidgetMut<'_, Box>) -> R, + f: impl FnOnce(WidgetMut<'_, dyn Widget>) -> R, ) -> R { self.render_root.edit_widget(id, f) } diff --git a/masonry/src/testing/helper_widgets.rs b/masonry/src/testing/helper_widgets.rs index e906a3b7..6238fbed 100644 --- a/masonry/src/testing/helper_widgets.rs +++ b/masonry/src/testing/helper_widgets.rs @@ -18,7 +18,7 @@ use tracing::trace_span; use vello::Scene; use crate::event::{PointerEvent, TextEvent}; -use crate::widget::widget::{find_widget_at_pos, AsDynWidget as _}; +use crate::widget::widget::find_widget_at_pos; use crate::widget::{SizedBox, WidgetRef}; use crate::{ AccessCtx, AccessEvent, AsAny, BoxConstraints, ComposeCtx, CursorIcon, EventCtx, LayoutCtx, @@ -65,10 +65,10 @@ pub struct ModularWidget { /// A widget that can replace its child on command pub struct ReplaceChild { - child: WidgetPod>, + child: WidgetPod, #[allow(dead_code)] // reason: This is probably bit-rotted code. Next version will SizedBox with WidgetMut instead. - replacer: Box WidgetPod>>, + replacer: Box WidgetPod>, } /// A wrapper widget that records each time one of its methods is called. @@ -406,13 +406,7 @@ impl Widget for ModularWidget { ctx: QueryCtx<'c>, pos: Point, ) -> Option> { - find_widget_at_pos( - &WidgetRef { - widget: self.as_dyn(), - ctx, - }, - pos, - ) + find_widget_at_pos(&WidgetRef { widget: self, ctx }, pos) } fn type_name(&self) -> &'static str { @@ -438,8 +432,8 @@ impl ReplaceChild { /// The `child` is the initial child widget, and `f` is a function that /// returns a new widget to replace it with. pub fn new(child: impl Widget, f: impl Fn() -> W + 'static) -> Self { - let child = WidgetPod::new(child).boxed(); - let replacer = Box::new(move || WidgetPod::new(f()).boxed()); + let child = WidgetPod::new(child).erased(); + let replacer = Box::new(move || WidgetPod::new(f()).erased()); Self { child, replacer } } } diff --git a/masonry/src/widget/align.rs b/masonry/src/widget/align.rs index dbef6af3..f6d7ec43 100644 --- a/masonry/src/widget/align.rs +++ b/masonry/src/widget/align.rs @@ -26,7 +26,7 @@ use crate::{ /// A widget that aligns its child. pub struct Align { align: UnitPoint, - child: WidgetPod>, + child: WidgetPod, width_factor: Option, height_factor: Option, } @@ -41,7 +41,7 @@ impl Align { pub fn new(align: UnitPoint, child: impl Widget + 'static) -> Self { Self { align, - child: WidgetPod::new(child).boxed(), + child: WidgetPod::new(child).erased(), width_factor: None, height_factor: None, } @@ -66,7 +66,7 @@ impl Align { pub fn horizontal(align: UnitPoint, child: impl Widget + 'static) -> Self { Self { align, - child: WidgetPod::new(child).boxed(), + child: WidgetPod::new(child).erased(), width_factor: None, height_factor: Some(1.0), } @@ -76,7 +76,7 @@ impl Align { pub fn vertical(align: UnitPoint, child: impl Widget + 'static) -> Self { Self { align, - child: WidgetPod::new(child).boxed(), + child: WidgetPod::new(child).erased(), width_factor: Some(1.0), height_factor: None, } diff --git a/masonry/src/widget/flex.rs b/masonry/src/widget/flex.rs index 2e1d6d08..71d42589 100644 --- a/masonry/src/widget/flex.rs +++ b/masonry/src/widget/flex.rs @@ -103,11 +103,11 @@ struct Spacing { enum Child { Fixed { - widget: WidgetPod>, + widget: WidgetPod, alignment: Option, }, Flex { - widget: WidgetPod>, + widget: WidgetPod, alignment: Option, flex: f64, }, @@ -214,17 +214,17 @@ impl Flex { /// /// Convenient for assembling a group of widgets in a single expression. pub fn with_child(self, child: impl Widget) -> Self { - self.with_child_pod(WidgetPod::new(Box::new(child))) + self.with_child_pod(WidgetPod::new(child).erased()) } /// Builder-style variant of [`Flex::add_child`], that takes the id that the child will have. /// /// Useful for unit tests. pub fn with_child_id(self, child: impl Widget, id: WidgetId) -> Self { - self.with_child_pod(WidgetPod::new_with_id(Box::new(child), id)) + self.with_child_pod(WidgetPod::new_with_id(child, id).erased()) } - pub fn with_child_pod(mut self, widget: WidgetPod>) -> Self { + pub fn with_child_pod(mut self, widget: WidgetPod) -> Self { let child = Child::Fixed { widget, alignment: None, @@ -235,13 +235,13 @@ impl Flex { /// Builder-style method to add a flexible child to the container. pub fn with_flex_child(self, child: impl Widget, params: impl Into) -> Self { - self.with_flex_child_pod(WidgetPod::new(Box::new(child)), params) + self.with_flex_child_pod(WidgetPod::new(child).erased(), params) } /// Builder-style method to add a flexible child to the container. pub fn with_flex_child_pod( mut self, - widget: WidgetPod>, + widget: WidgetPod, params: impl Into, ) -> Self { // TODO - dedup? @@ -378,7 +378,7 @@ impl Flex { /// [`with_child`]: Flex::with_child pub fn add_child(this: &mut WidgetMut<'_, Self>, child: impl Widget) { let child = Child::Fixed { - widget: WidgetPod::new(Box::new(child)), + widget: WidgetPod::new(child).erased(), alignment: None, }; this.widget.children.push(child); @@ -387,7 +387,7 @@ impl Flex { pub fn add_child_id(this: &mut WidgetMut<'_, Self>, child: impl Widget, id: WidgetId) { let child = Child::Fixed { - widget: WidgetPod::new_with_id(Box::new(child), id), + widget: WidgetPod::new_with_id(child, id).erased(), alignment: None, }; this.widget.children.push(child); @@ -401,7 +401,7 @@ impl Flex { params: impl Into, ) { let params = params.into(); - let child = new_flex_child(params, WidgetPod::new(Box::new(child))); + let child = new_flex_child(params, WidgetPod::new(child).erased()); this.widget.children.push(child); this.ctx.children_changed(); @@ -453,14 +453,14 @@ impl Flex { /// /// [`with_child`]: Flex::with_child pub fn insert_child(this: &mut WidgetMut<'_, Self>, idx: usize, child: impl Widget) { - Self::insert_child_pod(this, idx, WidgetPod::new(Box::new(child))); + Self::insert_child_pod(this, idx, WidgetPod::new(child).erased()); } /// Add a non-flex child widget. pub fn insert_child_pod( this: &mut WidgetMut<'_, Self>, idx: usize, - widget: WidgetPod>, + widget: WidgetPod, ) { let child = Child::Fixed { widget, @@ -476,13 +476,13 @@ impl Flex { child: impl Widget, params: impl Into, ) { - Self::insert_flex_child_pod(this, idx, WidgetPod::new(Box::new(child)), params); + Self::insert_flex_child_pod(this, idx, WidgetPod::new(child).erased(), params); } pub fn insert_flex_child_pod( this: &mut WidgetMut<'_, Self>, idx: usize, - child: WidgetPod>, + child: WidgetPod, params: impl Into, ) { let child = new_flex_child(params.into(), child); @@ -542,7 +542,7 @@ impl Flex { pub fn child_mut<'t>( this: &'t mut WidgetMut<'_, Self>, idx: usize, - ) -> Option>> { + ) -> Option> { let child = match &mut this.widget.children[idx] { Child::Fixed { widget, .. } | Child::Flex { widget, .. } => widget, Child::FixedSpacer(..) => return None, @@ -860,13 +860,13 @@ impl From for FlexParams { } impl Child { - fn widget_mut(&mut self) -> Option<&mut WidgetPod>> { + fn widget_mut(&mut self) -> Option<&mut WidgetPod> { match self { Self::Fixed { widget, .. } | Self::Flex { widget, .. } => Some(widget), _ => None, } } - fn widget(&self) -> Option<&WidgetPod>> { + fn widget(&self) -> Option<&WidgetPod> { match self { Self::Fixed { widget, .. } | Self::Flex { widget, .. } => Some(widget), _ => None, @@ -882,7 +882,7 @@ fn axis_default_spacer(axis: Axis) -> f64 { } } -fn new_flex_child(params: FlexParams, widget: WidgetPod>) -> Child { +fn new_flex_child(params: FlexParams, widget: WidgetPod) -> Child { if let Some(flex) = params.flex { if flex.is_normal() && flex > 0.0 { Child::Flex { diff --git a/masonry/src/widget/grid.rs b/masonry/src/widget/grid.rs index 4deccb95..621abf5c 100644 --- a/masonry/src/widget/grid.rs +++ b/masonry/src/widget/grid.rs @@ -21,7 +21,7 @@ pub struct Grid { } struct Child { - widget: WidgetPod>, + widget: WidgetPod, x: i32, y: i32, width: i32, @@ -56,18 +56,14 @@ impl Grid { /// /// Convenient for assembling a group of widgets in a single expression. pub fn with_child(self, child: impl Widget, params: GridParams) -> Self { - self.with_child_pod(WidgetPod::new(Box::new(child)), params) + self.with_child_pod(WidgetPod::new(child).erased(), params) } pub fn with_child_id(self, child: impl Widget, id: WidgetId, params: GridParams) -> Self { - self.with_child_pod(WidgetPod::new_with_id(Box::new(child), id), params) + self.with_child_pod(WidgetPod::new_with_id(child, id).erased(), params) } - pub fn with_child_pod( - mut self, - widget: WidgetPod>, - params: GridParams, - ) -> Self { + pub fn with_child_pod(mut self, widget: WidgetPod, params: GridParams) -> Self { let child = Child { widget, x: params.x, @@ -82,10 +78,10 @@ impl Grid { // --- MARK: IMPL CHILD --- impl Child { - fn widget_mut(&mut self) -> Option<&mut WidgetPod>> { + fn widget_mut(&mut self) -> Option<&mut WidgetPod> { Some(&mut self.widget) } - fn widget(&self) -> Option<&WidgetPod>> { + fn widget(&self) -> Option<&WidgetPod> { Some(&self.widget) } @@ -97,7 +93,7 @@ impl Child { } } -fn new_grid_child(params: GridParams, widget: WidgetPod>) -> Child { +fn new_grid_child(params: GridParams, widget: WidgetPod) -> Child { Child { widget, x: params.x, @@ -149,7 +145,7 @@ impl Grid { /// /// [`with_child`]: Grid::with_child pub fn add_child(this: &mut WidgetMut<'_, Self>, child: impl Widget, params: GridParams) { - let child_pod: WidgetPod> = WidgetPod::new(Box::new(child)); + let child_pod: WidgetPod = WidgetPod::new(child).erased(); Self::insert_child_pod(this, child_pod, params); } @@ -159,14 +155,14 @@ impl Grid { id: WidgetId, params: GridParams, ) { - let child_pod: WidgetPod> = WidgetPod::new_with_id(Box::new(child), id); + let child_pod: WidgetPod = WidgetPod::new_with_id(child, id).erased(); Self::insert_child_pod(this, child_pod, params); } /// Add a child widget. pub fn insert_child_pod( this: &mut WidgetMut<'_, Self>, - widget: WidgetPod>, + widget: WidgetPod, params: GridParams, ) { let child = new_grid_child(params, widget); @@ -181,13 +177,13 @@ impl Grid { child: impl Widget, params: impl Into, ) { - Self::insert_grid_child_pod(this, idx, WidgetPod::new(Box::new(child)), params); + Self::insert_grid_child_pod(this, idx, WidgetPod::new(child).erased(), params); } pub fn insert_grid_child_pod( this: &mut WidgetMut<'_, Self>, idx: usize, - child: WidgetPod>, + child: WidgetPod, params: impl Into, ) { let child = new_grid_child(params.into(), child); @@ -214,7 +210,7 @@ impl Grid { pub fn child_mut<'t>( this: &'t mut WidgetMut<'_, Self>, idx: usize, - ) -> Option>> { + ) -> Option> { let child = this.widget.children[idx].widget_mut()?; Some(this.ctx.get_mut(child)) } diff --git a/masonry/src/widget/portal.rs b/masonry/src/widget/portal.rs index 2bbe45c3..fc99eff5 100644 --- a/masonry/src/widget/portal.rs +++ b/masonry/src/widget/portal.rs @@ -13,8 +13,9 @@ use vello::Scene; use crate::widget::{Axis, ScrollBar, WidgetMut}; use crate::{ - AccessCtx, AccessEvent, BoxConstraints, ComposeCtx, EventCtx, LayoutCtx, PaintCtx, - PointerEvent, QueryCtx, RegisterCtx, TextEvent, Update, UpdateCtx, Widget, WidgetId, WidgetPod, + AccessCtx, AccessEvent, BoxConstraints, ComposeCtx, EventCtx, FromDynWidget, LayoutCtx, + PaintCtx, PointerEvent, QueryCtx, RegisterCtx, TextEvent, Update, UpdateCtx, Widget, WidgetId, + WidgetPod, }; // TODO - refactor - see https://github.com/linebender/xilem/issues/366 @@ -22,7 +23,7 @@ use crate::{ // TODO - Document which cases need request_layout, request_compose and request_render // Conceptually, a Portal is a Widget giving a restricted view of a child widget // Imagine a very large widget, and a rect that represents the part of the widget we see -pub struct Portal { +pub struct Portal { child: WidgetPod, // TODO - differentiate between the "explicit" viewport pos determined // by user input, and the computed viewport pos that may change based @@ -44,7 +45,9 @@ impl Portal { pub fn new(child: W) -> Self { Self::new_pod(WidgetPod::new(child)) } +} +impl Portal { pub fn new_pod(child: WidgetPod) -> Self { Self { child, @@ -127,7 +130,7 @@ fn compute_pan_range(mut viewport: Range, target: Range) -> Range viewport } -impl Portal { +impl Portal { // TODO - rename fn set_viewport_pos_raw(&mut self, portal_size: Size, content_size: Size, pos: Point) -> bool { let viewport_max_pos = @@ -167,7 +170,7 @@ impl Portal { } // --- MARK: WIDGETMUT --- -impl Portal { +impl Portal { pub fn child_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> WidgetMut<'t, W> { this.ctx.get_mut(&mut this.widget.child) } @@ -256,7 +259,7 @@ impl Portal { } // --- MARK: IMPL WIDGET --- -impl Widget for Portal { +impl Widget for Portal { fn on_pointer_event(&mut self, ctx: &mut EventCtx, event: &PointerEvent) { const SCROLLING_SPEED: f64 = 10.0; diff --git a/masonry/src/widget/root_widget.rs b/masonry/src/widget/root_widget.rs index 83cc1fcb..266415cd 100644 --- a/masonry/src/widget/root_widget.rs +++ b/masonry/src/widget/root_widget.rs @@ -9,13 +9,13 @@ use vello::Scene; use crate::widget::{WidgetMut, WidgetPod}; use crate::{ - AccessCtx, AccessEvent, BoxConstraints, EventCtx, LayoutCtx, PaintCtx, PointerEvent, QueryCtx, - RegisterCtx, Size, TextEvent, Widget, WidgetId, + AccessCtx, AccessEvent, BoxConstraints, EventCtx, FromDynWidget, LayoutCtx, PaintCtx, + PointerEvent, QueryCtx, RegisterCtx, Size, TextEvent, Widget, WidgetId, }; // TODO: This is a hack to provide an accessibility node with a Window type. // This should eventually be removed. -pub struct RootWidget { +pub struct RootWidget { pub(crate) pod: WidgetPod, } @@ -25,19 +25,21 @@ impl RootWidget { pod: WidgetPod::new(widget), } } +} +impl RootWidget { pub fn from_pod(pod: WidgetPod) -> Self { Self { pod } } } -impl RootWidget { +impl RootWidget { pub fn child_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> WidgetMut<'t, W> { this.ctx.get_mut(&mut this.widget.pod) } } -impl Widget for RootWidget { +impl Widget for RootWidget { fn on_pointer_event(&mut self, _ctx: &mut EventCtx, _event: &PointerEvent) {} fn on_text_event(&mut self, _ctx: &mut EventCtx, _event: &TextEvent) {} fn on_access_event(&mut self, _ctx: &mut EventCtx, _event: &AccessEvent) {} diff --git a/masonry/src/widget/sized_box.rs b/masonry/src/widget/sized_box.rs index bd628c70..658d7fd0 100644 --- a/masonry/src/widget/sized_box.rs +++ b/masonry/src/widget/sized_box.rs @@ -57,7 +57,7 @@ pub struct Padding { /// and width as possible given the parent's constraints. If height or width is not set, /// it will be treated as zero. pub struct SizedBox { - child: Option>>, + child: Option>, width: Option, height: Option, background: Option, @@ -186,7 +186,7 @@ impl SizedBox { /// Construct container with child, and both width and height not set. pub fn new(child: impl Widget) -> Self { Self { - child: Some(WidgetPod::new(child).boxed()), + child: Some(WidgetPod::new(child).erased()), width: None, height: None, background: None, @@ -199,7 +199,7 @@ impl SizedBox { /// Construct container with child, and both width and height not set. pub fn new_with_id(child: impl Widget, id: WidgetId) -> Self { Self { - child: Some(WidgetPod::new_with_id(child, id).boxed()), + child: Some(WidgetPod::new_with_id(child, id).erased()), width: None, height: None, background: None, @@ -210,7 +210,7 @@ impl SizedBox { } /// Construct container with child in a pod, and both width and height not set. - pub fn new_pod(child: WidgetPod>) -> Self { + pub fn new_pod(child: WidgetPod) -> Self { Self { child: Some(child), width: None, @@ -333,7 +333,7 @@ impl SizedBox { if let Some(child) = this.widget.child.take() { this.ctx.remove_child(child); } - this.widget.child = Some(WidgetPod::new(child).boxed()); + this.widget.child = Some(WidgetPod::new(child).erased()); this.ctx.children_changed(); this.ctx.request_layout(); } @@ -423,9 +423,7 @@ impl SizedBox { } // TODO - Doc - pub fn child_mut<'t>( - this: &'t mut WidgetMut<'_, Self>, - ) -> Option>> { + pub fn child_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> Option> { let child = this.widget.child.as_mut()?; Some(this.ctx.get_mut(child)) } diff --git a/masonry/src/widget/split.rs b/masonry/src/widget/split.rs index acc6ea6d..f96b7cf2 100644 --- a/masonry/src/widget/split.rs +++ b/masonry/src/widget/split.rs @@ -34,8 +34,8 @@ pub struct Split { /// bar was clicked. This is used to ensure a click without mouse move is a no-op, /// instead of re-centering the bar on the mouse. click_offset: f64, - child1: WidgetPod>, - child2: WidgetPod>, + child1: WidgetPod, + child2: WidgetPod, } // --- MARK: BUILDERS --- @@ -55,8 +55,8 @@ impl Split { solid: false, draggable: false, click_offset: 0.0, - child1: WidgetPod::new(child1).boxed(), - child2: WidgetPod::new(child2).boxed(), + child1: WidgetPod::new(child1).erased(), + child2: WidgetPod::new(child2).erased(), } } diff --git a/masonry/src/widget/tests/transforms.rs b/masonry/src/widget/tests/transforms.rs index f0ec1da7..b62434fb 100644 --- a/masonry/src/widget/tests/transforms.rs +++ b/masonry/src/widget/tests/transforms.rs @@ -12,12 +12,14 @@ use crate::testing::TestHarness; use crate::widget::{Alignment, Button, ChildAlignment, Label, SizedBox, ZStack}; use crate::{assert_render_snapshot, PointerButton, Widget, WidgetPod}; -fn blue_box(inner: impl Widget) -> SizedBox { - SizedBox::new(inner) - .width(200.) - .height(100.) - .background(palette::css::BLUE) - .border(palette::css::TEAL, 2.) +fn blue_box(inner: impl Widget) -> Box { + Box::new( + SizedBox::new(inner) + .width(200.) + .height(100.) + .background(palette::css::BLUE) + .border(palette::css::TEAL, 2.), + ) } #[test] @@ -31,7 +33,7 @@ fn transforms_translation_rotation() { .then_rotate(PI * 0.25) .then_translate(translation), ) - .boxed(); + .erased(); let widget = ZStack::new().with_child_pod(transformed_widget, ChildAlignment::ParentAligned); let mut harness = TestHarness::create(widget); @@ -46,7 +48,7 @@ fn transforms_pointer_events() { ), Affine::rotate(PI * 0.125).then_translate(Vec2::new(100.0, 50.0)), ) - .boxed(); + .erased(); let widget = ZStack::new().with_child_pod(transformed_widget, ChildAlignment::ParentAligned); let mut harness = TestHarness::create(widget); diff --git a/masonry/src/widget/widget.rs b/masonry/src/widget/widget.rs index 59fb1cb3..59e376f3 100644 --- a/masonry/src/widget/widget.rs +++ b/masonry/src/widget/widget.rs @@ -4,7 +4,6 @@ use std::any::Any; use std::fmt::Display; use std::num::NonZeroU64; -use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicU64, Ordering}; use accesskit::{Node, Role}; @@ -53,11 +52,16 @@ impl WidgetId { /// A trait to access a `Widget` as trait object. It is implemented for all types that implement `Widget`. pub trait AsDynWidget { + fn as_box_dyn(self: Box) -> Box; fn as_dyn(&self) -> &dyn Widget; fn as_mut_dyn(&mut self) -> &mut dyn Widget; } impl AsDynWidget for T { + fn as_box_dyn(self: Box) -> Box { + self + } + fn as_dyn(&self) -> &dyn Widget { self as &dyn Widget } @@ -67,6 +71,34 @@ impl AsDynWidget for T { } } +/// A trait that lets functions either downcast to a `Sized` widget or keep a `dyn Widget`. +pub trait FromDynWidget { + /// Downcast `widget` if `Self: Sized`, else return it as-is. + fn from_dyn(widget: &dyn Widget) -> Option<&Self>; + /// Downcast `widget` if `Self: Sized`, else return it as-is. + fn from_dyn_mut(widget: &mut dyn Widget) -> Option<&mut Self>; +} + +impl FromDynWidget for T { + fn from_dyn(widget: &dyn Widget) -> Option<&Self> { + widget.as_any().downcast_ref() + } + + fn from_dyn_mut(widget: &mut dyn Widget) -> Option<&mut Self> { + widget.as_mut_any().downcast_mut() + } +} + +impl FromDynWidget for dyn Widget { + fn from_dyn(widget: &dyn Widget) -> Option<&Self> { + Some(widget) + } + + fn from_dyn_mut(widget: &mut dyn Widget) -> Option<&mut Self> { + Some(widget) + } +} + // TODO - Add tutorial: implementing a widget - See https://github.com/linebender/xilem/issues/376 /// The trait implemented by all widgets. /// @@ -443,103 +475,3 @@ impl Display for WidgetId { write!(f, "#{}", self.0) } } - -#[warn(clippy::missing_trait_methods)] -// TODO - remove -impl Widget for Box { - fn on_pointer_event(&mut self, ctx: &mut EventCtx, event: &PointerEvent) { - self.deref_mut().on_pointer_event(ctx, event); - } - - fn on_text_event(&mut self, ctx: &mut EventCtx, event: &TextEvent) { - self.deref_mut().on_text_event(ctx, event); - } - - fn on_access_event(&mut self, ctx: &mut EventCtx, event: &AccessEvent) { - self.deref_mut().on_access_event(ctx, event); - } - - fn on_anim_frame(&mut self, ctx: &mut UpdateCtx, interval: u64) { - self.deref_mut().on_anim_frame(ctx, interval); - } - - fn register_children(&mut self, ctx: &mut RegisterCtx) { - self.deref_mut().register_children(ctx); - } - - fn update(&mut self, ctx: &mut UpdateCtx, event: &Update) { - self.deref_mut().update(ctx, event); - } - - fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size { - self.deref_mut().layout(ctx, bc) - } - - fn compose(&mut self, ctx: &mut ComposeCtx) { - self.deref_mut().compose(ctx); - } - - fn paint(&mut self, ctx: &mut PaintCtx, scene: &mut Scene) { - self.deref_mut().paint(ctx, scene); - } - - fn accessibility_role(&self) -> Role { - self.deref().accessibility_role() - } - - fn accessibility(&mut self, ctx: &mut AccessCtx, node: &mut Node) { - self.deref_mut().accessibility(ctx, node); - } - - fn type_name(&self) -> &'static str { - self.deref().type_name() - } - - fn short_type_name(&self) -> &'static str { - self.deref().short_type_name() - } - - fn children_ids(&self) -> SmallVec<[WidgetId; 16]> { - self.deref().children_ids() - } - - fn accepts_pointer_interaction(&self) -> bool { - self.deref().accepts_pointer_interaction() - } - - fn accepts_focus(&self) -> bool { - self.deref().accepts_focus() - } - - fn accepts_text_input(&self) -> bool { - self.deref().accepts_text_input() - } - - fn make_trace_span(&self, ctx: &QueryCtx<'_>) -> Span { - self.deref().make_trace_span(ctx) - } - - fn get_debug_text(&self) -> Option { - self.deref().get_debug_text() - } - - fn get_cursor(&self, ctx: &QueryCtx, pos: Point) -> CursorIcon { - self.deref().get_cursor(ctx, pos) - } - - fn find_widget_at_pos<'c>( - &'c self, - ctx: QueryCtx<'c>, - pos: Point, - ) -> Option> { - self.deref().find_widget_at_pos(ctx, pos) - } - - fn as_any(&self) -> &dyn Any { - self.deref().as_any() - } - - fn as_mut_any(&mut self) -> &mut dyn Any { - self.deref_mut().as_mut_any() - } -} diff --git a/masonry/src/widget/widget_mut.rs b/masonry/src/widget/widget_mut.rs index df577ea9..311b066f 100644 --- a/masonry/src/widget/widget_mut.rs +++ b/masonry/src/widget/widget_mut.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::contexts::MutateCtx; -use crate::{Affine, Widget}; +use crate::{Affine, FromDynWidget, Widget}; // TODO - Document extension trait workaround. // See https://xi.zulipchat.com/#narrow/stream/317477-masonry/topic/Thoughts.20on.20simplifying.20WidgetMut/near/436478885 @@ -23,12 +23,12 @@ use crate::{Affine, Widget}; /// /// Once the Receiver trait is stabilized, `WidgetMut` will implement it so that custom /// widgets in downstream crates can use `WidgetMut` as the receiver for inherent methods. -pub struct WidgetMut<'a, W: Widget> { +pub struct WidgetMut<'a, W: Widget + ?Sized> { pub ctx: MutateCtx<'a>, pub widget: &'a mut W, } -impl Drop for WidgetMut<'_, W> { +impl Drop for WidgetMut<'_, W> { fn drop(&mut self) { // If this `WidgetMut` is a reborrow, a parent non-reborrow `WidgetMut` // still exists which will do the merge-up in `Drop`. @@ -38,7 +38,7 @@ impl Drop for WidgetMut<'_, W> { } } -impl WidgetMut<'_, W> { +impl WidgetMut<'_, W> { /// Get a `WidgetMut` for the same underlying widget with a shorter lifetime. pub fn reborrow_mut(&mut self) -> WidgetMut<'_, W> { let widget = &mut self.widget; @@ -54,14 +54,14 @@ impl WidgetMut<'_, W> { pub fn set_transform(&mut self, transform: Affine) { self.ctx.set_transform(transform); } -} -impl WidgetMut<'_, Box> { /// Attempt to downcast to `WidgetMut` of concrete Widget type. - pub fn try_downcast(&mut self) -> Option> { + pub fn try_downcast( + &mut self, + ) -> Option> { Some(WidgetMut { ctx: self.ctx.reborrow_mut(), - widget: self.widget.as_mut_any().downcast_mut()?, + widget: W2::from_dyn_mut(self.widget.as_mut_dyn())?, }) } @@ -71,9 +71,9 @@ impl WidgetMut<'_, Box> { /// /// Panics if the downcast fails, with an error message that shows the /// discrepancy between the expected and actual types. - pub fn downcast(&mut self) -> WidgetMut<'_, W2> { + pub fn downcast(&mut self) -> WidgetMut<'_, W2> { let w1_name = self.widget.type_name(); - match self.widget.as_mut_any().downcast_mut() { + match W2::from_dyn_mut(self.widget.as_mut_dyn()) { Some(widget) => WidgetMut { ctx: self.ctx.reborrow_mut(), widget, diff --git a/masonry/src/widget/widget_pod.rs b/masonry/src/widget/widget_pod.rs index 61476d2b..d9183dd8 100644 --- a/masonry/src/widget/widget_pod.rs +++ b/masonry/src/widget/widget_pod.rs @@ -9,7 +9,7 @@ use crate::{Affine, Widget, WidgetId}; /// but rather contain a `WidgetPod`, which has additional state needed /// for layout and for the widget to participate in event flow. // TODO - Add reference to container tutorial -pub struct WidgetPod { +pub struct WidgetPod { id: WidgetId, inner: WidgetPodInner, } @@ -20,12 +20,12 @@ pub struct WidgetPod { // through context methods where they already have access to the arena. // Implementing that requires solving non-trivial design questions. -pub(crate) struct CreateWidget { - pub(crate) widget: W, +pub(crate) struct CreateWidget { + pub(crate) widget: Box, pub(crate) transform: Affine, } -enum WidgetPodInner { +enum WidgetPodInner { Create(CreateWidget), Inserted, } @@ -42,15 +42,17 @@ impl WidgetPod { /// Create a new widget pod with fixed id. pub fn new_with_id(inner: W, id: WidgetId) -> Self { - Self::new_with_id_and_transform(inner, id, Affine::IDENTITY) + Self::new_with_id_and_transform(Box::new(inner), id, Affine::IDENTITY) } +} +impl WidgetPod { /// Create a new widget pod with a custom transform. - pub fn new_with_transform(inner: W, transform: Affine) -> Self { + pub fn new_with_transform(inner: Box, transform: Affine) -> Self { Self::new_with_id_and_transform(inner, WidgetId::next(), transform) } - pub fn new_with_id_and_transform(inner: W, id: WidgetId, transform: Affine) -> Self { + pub fn new_with_id_and_transform(inner: Box, id: WidgetId, transform: Affine) -> Self { Self { id, inner: WidgetPodInner::Create(CreateWidget { @@ -59,7 +61,6 @@ impl WidgetPod { }), } } - pub(crate) fn incomplete(&self) -> bool { matches!(self.inner, WidgetPodInner::Create(_)) } @@ -75,17 +76,23 @@ impl WidgetPod { pub fn id(&self) -> WidgetId { self.id } -} -impl WidgetPod { - /// Box the contained widget. + /// Type-erase the contained widget. /// - /// Convert a `WidgetPod` containing a widget of a specific concrete type - /// into a dynamically boxed widget. - pub fn boxed(self) -> WidgetPod> { + /// Convert a `WidgetPod` pointing to a widget of a specific concrete type + /// `WidgetPod` pointing to a `dyn Widget`. + pub fn erased(self) -> WidgetPod { let WidgetPodInner::Create(inner) = self.inner else { + // TODO - Enabling this case isn't impossible anymore. + // We're keeping it forbidden for now. panic!("Cannot box a widget after it has been inserted into the widget graph") }; - WidgetPod::new_with_id_and_transform(Box::new(inner.widget), self.id, inner.transform) + WidgetPod { + id: self.id, + inner: WidgetPodInner::Create(CreateWidget { + widget: inner.widget.as_box_dyn(), + transform: inner.transform, + }), + } } } diff --git a/masonry/src/widget/zstack.rs b/masonry/src/widget/zstack.rs index f2d40785..2d8bb964 100644 --- a/masonry/src/widget/zstack.rs +++ b/masonry/src/widget/zstack.rs @@ -12,7 +12,7 @@ use smallvec::SmallVec; use tracing::trace_span; struct Child { - widget: WidgetPod>, + widget: WidgetPod, alignment: ChildAlignment, } @@ -163,7 +163,7 @@ impl From for ChildAlignment { } impl Child { - fn new(widget: WidgetPod>, alignment: ChildAlignment) -> Self { + fn new(widget: WidgetPod, alignment: ChildAlignment) -> Self { Self { widget, alignment } } @@ -188,7 +188,7 @@ impl ZStack { /// Appends a child widget to the `ZStack`. /// The child are placed back to front, in the order they are added. pub fn with_child(self, child: impl Widget, alignment: impl Into) -> Self { - self.with_child_pod(WidgetPod::new(Box::new(child)), alignment) + self.with_child_pod(WidgetPod::new(child).erased(), alignment) } /// Appends a child widget with a given `id` to the `ZStack`. @@ -198,7 +198,7 @@ impl ZStack { id: WidgetId, alignment: impl Into, ) -> Self { - self.with_child_pod(WidgetPod::new_with_id(Box::new(child), id), alignment) + self.with_child_pod(WidgetPod::new_with_id(child, id).erased(), alignment) } /// Appends a child widget pod to the `ZStack`. @@ -206,7 +206,7 @@ impl ZStack { /// See also [`Self::with_child`] if the widget is not already wrapped in a [`WidgetPod`]. pub fn with_child_pod( mut self, - child: WidgetPod>, + child: WidgetPod, alignment: impl Into, ) -> Self { let child = Child::new(child, alignment.into()); @@ -226,7 +226,7 @@ impl ZStack { child: impl Widget, alignment: impl Into, ) { - let child_pod: WidgetPod> = WidgetPod::new(Box::new(child)); + let child_pod: WidgetPod = WidgetPod::new(child).erased(); Self::insert_child_pod(this, child_pod, alignment); } @@ -239,14 +239,14 @@ impl ZStack { id: WidgetId, alignment: impl Into, ) { - let child_pod: WidgetPod> = WidgetPod::new_with_id(Box::new(child), id); + let child_pod: WidgetPod = WidgetPod::new_with_id(child, id).erased(); Self::insert_child_pod(this, child_pod, alignment); } /// Add a child widget to the `ZStack`. pub fn insert_child_pod( this: &mut WidgetMut<'_, Self>, - widget: WidgetPod>, + widget: WidgetPod, alignment: impl Into, ) { let child = Child::new(widget, alignment.into()); @@ -266,7 +266,7 @@ impl ZStack { pub fn child_mut<'t>( this: &'t mut WidgetMut<'_, Self>, idx: usize, - ) -> Option>> { + ) -> Option> { let child = &mut this.widget.children[idx].widget; Some(this.ctx.get_mut(child)) } diff --git a/xilem/src/any_view.rs b/xilem/src/any_view.rs index 054c9a1b..16921aed 100644 --- a/xilem/src/any_view.rs +++ b/xilem/src/any_view.rs @@ -4,8 +4,8 @@ use accesskit::{Node, Role}; use masonry::widget::WidgetMut; use masonry::{ - AccessCtx, AccessEvent, BoxConstraints, EventCtx, LayoutCtx, PaintCtx, Point, PointerEvent, - QueryCtx, RegisterCtx, Size, TextEvent, Widget, WidgetId, WidgetPod, + AccessCtx, AccessEvent, BoxConstraints, EventCtx, FromDynWidget, LayoutCtx, PaintCtx, Point, + PointerEvent, QueryCtx, RegisterCtx, Size, TextEvent, Widget, WidgetId, WidgetPod, }; use smallvec::{smallvec, SmallVec}; use tracing::{trace_span, Span}; @@ -25,10 +25,10 @@ use crate::{Pod, ViewCtx}; pub type AnyWidgetView = dyn AnyView> + Send + Sync; -impl SuperElement, ViewCtx> for Pod { +impl SuperElement, ViewCtx> for Pod { fn upcast(ctx: &mut ViewCtx, child: Pod) -> Self { ctx.new_pod(DynWidget { - inner: child.boxed_widget_pod(), + inner: child.erased_widget_pod(), }) } @@ -46,26 +46,21 @@ impl SuperElement, ViewCtx> for Pod { } } -impl AnyElement, ViewCtx> for Pod { +impl AnyElement, ViewCtx> for Pod { fn replace_inner(mut this: Self::Mut<'_>, child: Pod) -> Self::Mut<'_> { - DynWidget::replace_inner(&mut this, child.boxed_widget_pod()); + DynWidget::replace_inner(&mut this, child.erased_widget_pod()); this } } /// A widget whose only child can be dynamically replaced. -/// -/// `WidgetPod>` doesn't expose this possibility. #[allow(unnameable_types)] // This is an implementation detail of `AnyWidgetView` pub struct DynWidget { - inner: WidgetPod>, + inner: WidgetPod, } impl DynWidget { - pub(crate) fn replace_inner( - this: &mut WidgetMut<'_, Self>, - widget: WidgetPod>, - ) { + pub(crate) fn replace_inner(this: &mut WidgetMut<'_, Self>, widget: WidgetPod) { let old_widget = std::mem::replace(&mut this.widget.inner, widget); this.ctx.remove_child(old_widget); } diff --git a/xilem/src/lib.rs b/xilem/src/lib.rs index f5f554a7..3f641dad 100644 --- a/xilem/src/lib.rs +++ b/xilem/src/lib.rs @@ -44,7 +44,7 @@ use std::sync::Arc; use masonry::dpi::LogicalSize; use masonry::widget::{RootWidget, WidgetMut}; -use masonry::{event_loop_runner, Widget, WidgetId, WidgetPod}; +use masonry::{event_loop_runner, FromDynWidget, Widget, WidgetId, WidgetPod}; use view::{transformed, Transformed}; use winit::error::EventLoopError; use winit::window::{Window, WindowAttributes}; @@ -186,8 +186,8 @@ where /// and so might not actually own the underlying widget value. /// When creating widgets in Xilem, layered views all want access to the - using /// `WidgetPod` for this purpose would require fallible unwrapping. -pub struct Pod { - pub widget: W, +pub struct Pod { + pub widget: Box, pub id: WidgetId, /// The transform the widget will be created with. /// @@ -198,36 +198,39 @@ pub struct Pod { pub transform: Affine, } -impl Pod { +impl Pod { fn new(widget: W) -> Self { Self { - widget, + widget: Box::new(widget), id: WidgetId::next(), transform: Default::default(), } } +} + +impl Pod { + fn erased(self) -> Pod { + Pod { + widget: self.widget.as_box_dyn(), + id: self.id, + transform: self.transform, + } + } fn into_widget_pod(self) -> WidgetPod { WidgetPod::new_with_id_and_transform(self.widget, self.id, self.transform) } - fn boxed_widget_pod(self) -> WidgetPod> { - WidgetPod::new_with_id_and_transform(Box::new(self.widget), self.id, self.transform) - } - fn boxed(self) -> Pod> { - Pod { - widget: Box::new(self.widget), - id: self.id, - transform: self.transform, - } + fn erased_widget_pod(self) -> WidgetPod { + WidgetPod::new_with_id_and_transform(self.widget, self.id, self.transform).erased() } } -impl ViewElement for Pod { +impl ViewElement for Pod { type Mut<'a> = WidgetMut<'a, W>; } -impl SuperElement, ViewCtx> for Pod> { +impl SuperElement, ViewCtx> for Pod { fn upcast(_: &mut ViewCtx, child: Pod) -> Self { - child.boxed() + child.erased() } fn with_downcast_val( @@ -243,7 +246,7 @@ impl SuperElement, ViewCtx> for Pod> { pub trait WidgetView: View> + Send + Sync { - type Widget: Widget; + type Widget: Widget + FromDynWidget + ?Sized; /// Returns a boxed type erased [`AnyWidgetView`] /// @@ -281,7 +284,7 @@ pub trait WidgetView: impl WidgetView for V where V: View> + Send + Sync, - W: Widget, + W: Widget + FromDynWidget + ?Sized, { type Widget = W; } @@ -337,18 +340,21 @@ impl ViewPathTracker for ViewCtx { } impl ViewCtx { - pub fn new_pod(&mut self, widget: W) -> Pod { + pub fn new_pod(&mut self, widget: W) -> Pod { Pod::new(widget) } - pub fn with_leaf_action_widget( + pub fn with_leaf_action_widget( &mut self, - f: impl FnOnce(&mut Self) -> Pod, - ) -> (Pod, ()) { + f: impl FnOnce(&mut Self) -> Pod, + ) -> (Pod, ()) { (self.with_action_widget(f), ()) } - pub fn with_action_widget(&mut self, f: impl FnOnce(&mut Self) -> Pod) -> Pod { + pub fn with_action_widget( + &mut self, + f: impl FnOnce(&mut Self) -> Pod, + ) -> Pod { let value = f(self); self.record_action(value.id); value @@ -360,7 +366,7 @@ impl ViewCtx { self.widget_map.insert(id, path); } - pub fn teardown_leaf(&mut self, widget: WidgetMut) { + pub fn teardown_leaf(&mut self, widget: WidgetMut) { self.widget_map.remove(&widget.ctx.widget_id()); } diff --git a/xilem/src/one_of.rs b/xilem/src/one_of.rs index 7be2d345..43b79d63 100644 --- a/xilem/src/one_of.rs +++ b/xilem/src/one_of.rs @@ -5,8 +5,8 @@ use accesskit::{Node, Role}; use masonry::{ - AccessCtx, AccessEvent, BoxConstraints, EventCtx, LayoutCtx, PaintCtx, Point, PointerEvent, - RegisterCtx, Size, TextEvent, Widget, WidgetId, WidgetPod, + AccessCtx, AccessEvent, BoxConstraints, EventCtx, FromDynWidget, LayoutCtx, PaintCtx, Point, + PointerEvent, RegisterCtx, Size, TextEvent, Widget, WidgetId, WidgetPod, }; use smallvec::{smallvec, SmallVec}; use vello::Scene; @@ -16,15 +16,15 @@ use crate::core::Mut; use crate::{Pod, ViewCtx}; impl< - A: Widget, - B: Widget, - C: Widget, - D: Widget, - E: Widget, - F: Widget, - G: Widget, - H: Widget, - I: Widget, + A: Widget + FromDynWidget + ?Sized, + B: Widget + FromDynWidget + ?Sized, + C: Widget + FromDynWidget + ?Sized, + D: Widget + FromDynWidget + ?Sized, + E: Widget + FromDynWidget + ?Sized, + F: Widget + FromDynWidget + ?Sized, + G: Widget + FromDynWidget + ?Sized, + H: Widget + FromDynWidget + ?Sized, + I: Widget + FromDynWidget + ?Sized, > crate::core::one_of::OneOfCtx< Pod, @@ -143,11 +143,21 @@ impl< } impl crate::core::one_of::PhantomElementCtx for ViewCtx { - type PhantomElement = Pod>; + type PhantomElement = Pod; } #[allow(unnameable_types)] // reason: Implementation detail, public because of trait visibility rules -pub enum OneOfWidget { +pub enum OneOfWidget< + A: ?Sized, + B: ?Sized, + C: ?Sized, + D: ?Sized, + E: ?Sized, + F: ?Sized, + G: ?Sized, + H: ?Sized, + I: ?Sized, +> { A(WidgetPod), B(WidgetPod), C(WidgetPod), @@ -160,15 +170,15 @@ pub enum OneOfWidget { } impl< - A: Widget, - B: Widget, - C: Widget, - D: Widget, - E: Widget, - F: Widget, - G: Widget, - H: Widget, - I: Widget, + A: Widget + FromDynWidget + ?Sized, + B: Widget + FromDynWidget + ?Sized, + C: Widget + FromDynWidget + ?Sized, + D: Widget + FromDynWidget + ?Sized, + E: Widget + FromDynWidget + ?Sized, + F: Widget + FromDynWidget + ?Sized, + G: Widget + FromDynWidget + ?Sized, + H: Widget + FromDynWidget + ?Sized, + I: Widget + FromDynWidget + ?Sized, > Widget for OneOfWidget { fn on_pointer_event(&mut self, _ctx: &mut EventCtx, _event: &PointerEvent) {} diff --git a/xilem/src/view/flex.rs b/xilem/src/view/flex.rs index 615d836f..b7c9343a 100644 --- a/xilem/src/view/flex.rs +++ b/xilem/src/view/flex.rs @@ -5,7 +5,7 @@ use std::marker::PhantomData; use masonry::widget::{self, WidgetMut}; pub use masonry::widget::{Axis, CrossAxisAlignment, FlexParams, MainAxisAlignment}; -use masonry::Widget; +use masonry::{FromDynWidget, Widget}; use crate::core::{ AppendVec, DynMessage, ElementSplice, MessageResult, Mut, SuperElement, View, ViewElement, @@ -107,7 +107,7 @@ where for child in elements.into_inner() { widget = match child { FlexElement::Child(child, params) => { - widget.with_flex_child_pod(child.into_widget_pod(), params) + widget.with_flex_child_pod(child.erased_widget_pod(), params) } FlexElement::FixedSpacer(size) => widget.with_spacer(size), FlexElement::FlexSpacer(flex) => widget.with_flex_spacer(flex), @@ -170,7 +170,7 @@ where } pub enum FlexElement { - Child(Pod>, FlexParams), + Child(Pod, FlexParams), FixedSpacer(f64), FlexSpacer(f64), } @@ -221,9 +221,9 @@ impl SuperElement for FlexElement { } } -impl SuperElement, ViewCtx> for FlexElement { +impl SuperElement, ViewCtx> for FlexElement { fn upcast(_: &mut ViewCtx, child: Pod) -> Self { - Self::Child(child.boxed(), FlexParams::default()) + Self::Child(child.erased(), FlexParams::default()) } fn with_downcast_val( @@ -248,7 +248,7 @@ impl ElementSplice for FlexSplice<'_> { widget::Flex::insert_flex_child_pod( &mut self.element, self.idx, - child.into_widget_pod(), + child.erased_widget_pod(), params, ); } @@ -270,7 +270,7 @@ impl ElementSplice for FlexSplice<'_> { widget::Flex::insert_flex_child_pod( &mut self.element, self.idx, - child.into_widget_pod(), + child.erased_widget_pod(), params, ); } @@ -455,7 +455,7 @@ where fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) { let (pod, state) = self.view.build(ctx); - (FlexElement::Child(pod.boxed(), self.params), state) + (FlexElement::Child(pod.erased(), self.params), state) } fn rebuild( @@ -740,7 +740,7 @@ where widget::Flex::insert_flex_child_pod( &mut element.parent, element.idx, - child.boxed_widget_pod(), + child.erased_widget_pod(), params, ); } else { diff --git a/xilem/src/view/grid.rs b/xilem/src/view/grid.rs index ffbc01fc..ccb2996c 100644 --- a/xilem/src/view/grid.rs +++ b/xilem/src/view/grid.rs @@ -4,7 +4,7 @@ use std::marker::PhantomData; use masonry::widget::{self, GridParams, WidgetMut}; -use masonry::Widget; +use masonry::{FromDynWidget, Widget}; use crate::core::{ AppendVec, DynMessage, ElementSplice, MessageResult, Mut, SuperElement, View, ViewElement, @@ -70,7 +70,7 @@ where for child in elements.into_inner() { widget = match child { GridElement::Child(child, params) => { - widget.with_child_pod(child.into_widget_pod(), params) + widget.with_child_pod(child.erased_widget_pod(), params) } } } @@ -151,14 +151,14 @@ impl SuperElement for GridElement { } } -impl SuperElement, ViewCtx> for GridElement { +impl SuperElement, ViewCtx> for GridElement { fn upcast(_: &mut ViewCtx, child: Pod) -> Self { // Getting here means that the widget didn't use .grid_item or .grid_pos. // This currently places the widget in the top left cell. // There is not much else, beyond purposefully failing, that can be done here, // because there isn't enough information to determine an appropriate spot // for the widget. - Self::Child(child.boxed(), GridParams::new(1, 1, 1, 1)) + Self::Child(child.erased(), GridParams::new(1, 1, 1, 1)) } fn with_downcast_val( @@ -186,7 +186,7 @@ impl ElementSplice for GridSplice<'_> { widget::Grid::insert_grid_child_pod( &mut self.element, self.idx, - child.into_widget_pod(), + child.erased_widget_pod(), params, ); } @@ -202,7 +202,7 @@ impl ElementSplice for GridSplice<'_> { widget::Grid::insert_grid_child_pod( &mut self.element, self.idx, - child.into_widget_pod(), + child.erased_widget_pod(), params, ); } @@ -305,7 +305,7 @@ pub trait GridExt: WidgetView { impl> GridExt for V {} pub enum GridElement { - Child(Pod>, GridParams), + Child(Pod, GridParams), } pub struct GridElementMut<'w> { @@ -367,7 +367,7 @@ where fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) { let (pod, state) = self.view.build(ctx); - (GridElement::Child(pod.boxed(), self.params), state) + (GridElement::Child(pod.erased(), self.params), state) } fn rebuild( diff --git a/xilem/src/view/sized_box.rs b/xilem/src/view/sized_box.rs index 829d3f2a..ad7fc2cd 100644 --- a/xilem/src/view/sized_box.rs +++ b/xilem/src/view/sized_box.rs @@ -133,7 +133,7 @@ where fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) { let (child, child_state) = self.inner.build(ctx); - let mut widget = widget::SizedBox::new_pod(child.boxed_widget_pod()) + let mut widget = widget::SizedBox::new_pod(child.erased_widget_pod()) .raw_width(self.width) .raw_height(self.height) .rounded(self.corner_radius) diff --git a/xilem/src/view/zstack.rs b/xilem/src/view/zstack.rs index 160a4dd8..30b86224 100644 --- a/xilem/src/view/zstack.rs +++ b/xilem/src/view/zstack.rs @@ -14,7 +14,7 @@ use crate::{ }; use masonry::{ widget::{self, Alignment, ChildAlignment, WidgetMut}, - Widget, + FromDynWidget, Widget, }; use xilem_core::{MessageResult, ViewId}; @@ -76,7 +76,7 @@ where let mut widget = widget::ZStack::new().with_alignment(self.alignment); let seq_state = self.sequence.seq_build(ctx, &mut elements); for child in elements.into_inner() { - widget = widget.with_child_pod(child.widget.into_widget_pod(), child.alignment); + widget = widget.with_child_pod(child.widget.erased_widget_pod(), child.alignment); } let pod = ctx.new_pod(widget); (pod, seq_state) @@ -181,7 +181,7 @@ where fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) { let (pod, state) = self.view.build(ctx); - (ZStackElement::new(pod.boxed(), self.alignment), state) + (ZStackElement::new(pod.erased(), self.alignment), state) } fn rebuild( @@ -232,7 +232,7 @@ where /// A struct implementing [`ViewElement`] for a `ZStack`. pub struct ZStackElement { - widget: Pod>, + widget: Pod, alignment: ChildAlignment, } @@ -243,7 +243,7 @@ pub struct ZStackElementMut<'w> { } impl ZStackElement { - fn new(widget: Pod>, alignment: ChildAlignment) -> Self { + fn new(widget: Pod, alignment: ChildAlignment) -> Self { Self { widget, alignment } } } @@ -273,9 +273,9 @@ impl SuperElement for ZStackElement { } } -impl SuperElement, ViewCtx> for ZStackElement { +impl SuperElement, ViewCtx> for ZStackElement { fn upcast(_: &mut ViewCtx, child: Pod) -> Self { - Self::new(child.boxed(), ChildAlignment::ParentAligned) + Self::new(child.erased(), ChildAlignment::ParentAligned) } fn with_downcast_val( @@ -331,7 +331,7 @@ impl ElementSplice for ZStackSplice<'_> { for element in self.scratch.drain() { widget::ZStack::insert_child_pod( &mut self.element, - element.widget.into_widget_pod(), + element.widget.erased_widget_pod(), element.alignment, ); self.idx += 1; @@ -342,7 +342,7 @@ impl ElementSplice for ZStackSplice<'_> { fn insert(&mut self, element: ZStackElement) { widget::ZStack::insert_child_pod( &mut self.element, - element.widget.into_widget_pod(), + element.widget.erased_widget_pod(), element.alignment, ); self.idx += 1; diff --git a/xilem_core/examples/user_interface.rs b/xilem_core/examples/user_interface.rs index a59d521a..bbc61e93 100644 --- a/xilem_core/examples/user_interface.rs +++ b/xilem_core/examples/user_interface.rs @@ -25,7 +25,7 @@ fn main() { trait Widget: 'static + Any { fn as_mut_any(&mut self) -> &mut dyn Any; } -struct WidgetPod { +struct WidgetBox { widget: W, } struct WidgetMut<'a, W: Widget> { @@ -41,18 +41,18 @@ impl Widget for Box { // Hmm, this implementation can't exist in `xilem` if `xilem_core` and/or `masonry` are a different crate // due to the orphan rules... -impl ViewElement for WidgetPod { +impl ViewElement for WidgetBox { type Mut<'a> = WidgetMut<'a, W>; } impl ViewMarker for Button {} impl View for Button { - type Element = WidgetPod; + type Element = WidgetBox; type ViewState = (); fn build(&self, _ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) { ( - WidgetPod { + WidgetBox { widget: ButtonWidget {}, }, (), @@ -98,15 +98,15 @@ impl Widget for ButtonWidget { } } -impl SuperElement, ViewCtx> for WidgetPod> { - fn upcast(_ctx: &mut ViewCtx, child: WidgetPod) -> Self { - WidgetPod { +impl SuperElement, ViewCtx> for WidgetBox> { + fn upcast(_ctx: &mut ViewCtx, child: WidgetBox) -> Self { + WidgetBox { widget: Box::new(child.widget), } } fn with_downcast_val( this: Self::Mut<'_>, - f: impl FnOnce( as ViewElement>::Mut<'_>) -> R, + f: impl FnOnce( as ViewElement>::Mut<'_>) -> R, ) -> (Self::Mut<'_>, R) { let value = WidgetMut { value: this.value.as_mut_any().downcast_mut().expect( @@ -137,14 +137,14 @@ impl ViewPathTracker for ViewCtx { } trait WidgetView: - View> + Send + Sync + View> + Send + Sync { type Widget: Widget + Send + Sync; } impl WidgetView for V where - V: View> + Send + Sync, + V: View> + Send + Sync, W: Widget + Send + Sync, { type Widget = W;