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;