Remove `Box<dyn Widget>` from a few places (#837)

Having `Box<dyn Widget>` 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<dyn Widget>` (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<dyn Widget>` 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<dyn Widget>`.
- Replace all instances of `WidgetPod<Box<dyn Widget>>` with
`WidgetPod<dyn Widget>`.
- Replace all instances of `xilem::Pod<Box<dyn Widget>>` with
`xilem::Pod<dyn Widget>`.
- Rename WidgetPod to WidgetBox in xilem_core example to avoid
ambiguity.
This commit is contained in:
Olivier FAURE 2025-01-20 12:52:08 +00:00 committed by GitHub
parent d03873d9d7
commit 9ffc650983
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 335 additions and 410 deletions

View File

@ -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::<Child>().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<impl Widget + ?Sized>) -> &'_ 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<Child: Widget>(&self, child: &'_ WidgetPod<Child>) -> &'_ WidgetState {
fn get_child_state(&self, child: &'_ WidgetPod<impl Widget + ?Sized>) -> &'_ 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<Child: Widget>(
fn get_child_state_mut<Child: Widget + ?Sized>(
&mut self,
child: &'_ mut WidgetPod<Child>,
) -> &'_ 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<Child>,
) -> 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<impl Widget>, method_name: &str) {
fn assert_layout_done(&self, child: &WidgetPod<impl Widget + ?Sized>, 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<impl Widget>, method_name: &str) {
fn assert_placed(&self, child: &WidgetPod<impl Widget + ?Sized>, 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<W: Widget>(&mut self, child: &mut WidgetPod<W>, bc: &BoxConstraints) -> Size {
pub fn run_layout(
&mut self,
child: &mut WidgetPod<impl Widget + ?Sized>,
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<W: Widget>(&mut self, child: &mut WidgetPod<W>, origin: Point) {
pub fn place_child(&mut self, child: &mut WidgetPod<impl Widget + ?Sized>, 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<impl Widget>,
child: &WidgetPod<impl Widget + ?Sized>,
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<impl Widget>) -> bool {
pub fn child_needs_layout(&self, child: &WidgetPod<impl Widget + ?Sized>) -> 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<impl Widget>) -> f64 {
pub fn child_baseline_offset(&self, child: &WidgetPod<impl Widget + ?Sized>) -> 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<impl Widget>) -> Rect {
pub fn child_layout_rect(&self, child: &WidgetPod<impl Widget + ?Sized>) -> 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<impl Widget>) -> Rect {
pub fn child_paint_rect(&self, child: &WidgetPod<impl Widget + ?Sized>) -> 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<impl Widget>) -> Size {
pub fn child_size(&self, child: &WidgetPod<impl Widget + ?Sized>) -> 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<impl Widget>) {
pub fn skip_layout(&mut self, child: &mut WidgetPod<impl Widget + ?Sized>) {
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<W: Widget>(
pub fn set_child_scroll_translation(
&mut self,
child: &mut WidgetPod<W>,
child: &mut WidgetPod<impl Widget + ?Sized>,
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<impl Widget>) {
pub fn remove_child(&mut self, child: WidgetPod<impl Widget + ?Sized>) {
// 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<impl Widget>, stashed: bool) {
pub fn set_stashed(&mut self, child: &mut WidgetPod<impl Widget + ?Sized>, 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<dyn Widget>>) + 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<W: Widget>(
pub fn mutate_later<W: Widget + FromDynWidget + ?Sized>(
&mut self,
child: &mut WidgetPod<W>,
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<impl Widget>) {
pub fn register_child(&mut self, child: &mut WidgetPod<impl Widget + ?Sized>) {
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<Child>,
) -> 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<Child>,
) -> 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<Child>,
) -> 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<Ctx, W> RawWrapper<'_, Ctx, W> {
impl<Ctx, W: ?Sized> RawWrapper<'_, Ctx, W> {
pub fn widget(&self) -> &W {
self.widget
}
@ -1334,7 +1348,7 @@ impl<Ctx, W> RawWrapper<'_, Ctx, W> {
}
#[allow(missing_docs, reason = "RawWrapper is likely to be reworked")]
impl<Ctx: IsContext, W> RawWrapperMut<'_, Ctx, W> {
impl<Ctx: IsContext, W: ?Sized> RawWrapperMut<'_, Ctx, W> {
pub fn widget(&mut self) -> &mut W {
self.widget
}
@ -1344,7 +1358,7 @@ impl<Ctx: IsContext, W> RawWrapperMut<'_, Ctx, W> {
}
}
impl<Ctx: IsContext, W> Drop for RawWrapperMut<'_, Ctx, W> {
impl<Ctx: IsContext, W: ?Sized> Drop for RawWrapperMut<'_, Ctx, W> {
fn drop(&mut self) {
self.parent_widget_state
.merge_up(self.ctx.get_widget_state());

View File

@ -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<Box<dyn Widget>>` 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<dyn Widget>` 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<...>>`.
- `RootWidget::child_mut()` returns a `WidgetMut<Portal<...>>`.
- `Portal::child_mut()` returns a `WidgetMut<Flex>`.

View File

@ -28,7 +28,7 @@ As an example, let's write a `VerticalStack` widget, which lays out its children
```rust,ignore
struct VerticalStack {
children: Vec<WidgetPod<Box<dyn Widget>>>,
children: Vec<WidgetPod<dyn Widget>>,
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<Box<dyn Widget>>) {
pub fn add_child(this: &mut WidgetMut<'_, Self>, child: WidgetPod<dyn Widget>) {
this.widget.children.push(child);
this.ctx.children_changed();
}

View File

@ -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;

View File

@ -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();

View File

@ -75,7 +75,7 @@ fn run_event_pass<E>(
);
}
pass_fn(widget, &mut ctx, event);
pass_fn(&mut **widget, &mut ctx, event);
is_handled = ctx.is_handled;
}

View File

@ -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<W: Widget>(
pub(crate) fn run_layout_on<W: Widget + ?Sized>(
parent_ctx: &mut LayoutCtx<'_>,
pod: &mut WidgetPod<W>,
bc: &BoxConstraints,

View File

@ -11,7 +11,7 @@ use crate::{MutateCtx, Widget, WidgetId};
pub(crate) fn mutate_widget<R>(
root: &mut RenderRoot,
id: WidgetId,
mutate_fn: impl FnOnce(WidgetMut<'_, Box<dyn Widget>>) -> 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<R>(
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);

View File

@ -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 {

View File

@ -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<Box<dyn Widget>>,
pub(crate) root: WidgetPod<dyn Widget>,
/// 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<dyn FnOnce(WidgetMut<'_, Box<dyn Widget>>)>,
pub(crate) callback: Box<dyn FnOnce(WidgetMut<'_, dyn Widget>)>,
}
/// 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<dyn Widget>, but the "true"
// type of our root widget is *also* Box<dyn Widget>. We downcast so we
// don't add one more level of indirection to this.
let widget = widget_ref
.item
.as_dyn_any()
.downcast_ref::<Box<dyn Widget>>()
.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> -> &dyn Widget
// Without this step, the type of `WidgetRef::widget` would be
// `&Box<dyn Widget> 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<R>(
&mut self,
f: impl FnOnce(WidgetMut<'_, Box<dyn Widget>>) -> R,
) -> R {
let res = mutate_widget(self, self.root.id(), |mut widget_mut| {
// Our WidgetArena stores all widgets as Box<dyn Widget>, but the "true"
// type of our root widget is *also* Box<dyn Widget>. 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::<Box<dyn Widget>>()
.unwrap();
let widget_mut = WidgetMut {
ctx: widget_mut.ctx.reborrow_mut(),
widget,
};
f(widget_mut)
});
pub fn edit_root_widget<R>(&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<R>(
&mut self,
id: WidgetId,
f: impl FnOnce(WidgetMut<'_, Box<dyn Widget>>) -> R,
f: impl FnOnce(WidgetMut<'_, dyn Widget>) -> R,
) -> R {
let res = mutate_widget(self, id, f);

View File

@ -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<R>(
&mut self,
f: impl FnOnce(WidgetMut<'_, Box<dyn Widget>>) -> R,
) -> R {
pub fn edit_root_widget<R>(&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<R>(
&mut self,
id: WidgetId,
f: impl FnOnce(WidgetMut<'_, Box<dyn Widget>>) -> R,
f: impl FnOnce(WidgetMut<'_, dyn Widget>) -> R,
) -> R {
self.render_root.edit_widget(id, f)
}

View File

@ -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<S> {
/// A widget that can replace its child on command
pub struct ReplaceChild {
child: WidgetPod<Box<dyn Widget>>,
child: WidgetPod<dyn Widget>,
#[allow(dead_code)]
// reason: This is probably bit-rotted code. Next version will SizedBox with WidgetMut instead.
replacer: Box<dyn Fn() -> WidgetPod<Box<dyn Widget>>>,
replacer: Box<dyn Fn() -> WidgetPod<dyn Widget>>,
}
/// A wrapper widget that records each time one of its methods is called.
@ -406,13 +406,7 @@ impl<S: 'static> Widget for ModularWidget<S> {
ctx: QueryCtx<'c>,
pos: Point,
) -> Option<WidgetRef<'c, dyn Widget>> {
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<W: Widget + 'static>(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 }
}
}

View File

@ -26,7 +26,7 @@ use crate::{
/// A widget that aligns its child.
pub struct Align {
align: UnitPoint,
child: WidgetPod<Box<dyn Widget>>,
child: WidgetPod<dyn Widget>,
width_factor: Option<f64>,
height_factor: Option<f64>,
}
@ -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,
}

View File

@ -103,11 +103,11 @@ struct Spacing {
enum Child {
Fixed {
widget: WidgetPod<Box<dyn Widget>>,
widget: WidgetPod<dyn Widget>,
alignment: Option<CrossAxisAlignment>,
},
Flex {
widget: WidgetPod<Box<dyn Widget>>,
widget: WidgetPod<dyn Widget>,
alignment: Option<CrossAxisAlignment>,
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<Box<dyn Widget>>) -> Self {
pub fn with_child_pod(mut self, widget: WidgetPod<dyn Widget>) -> 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<FlexParams>) -> 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<Box<dyn Widget>>,
widget: WidgetPod<dyn Widget>,
params: impl Into<FlexParams>,
) -> 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<FlexParams>,
) {
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<Box<dyn Widget>>,
widget: WidgetPod<dyn Widget>,
) {
let child = Child::Fixed {
widget,
@ -476,13 +476,13 @@ impl Flex {
child: impl Widget,
params: impl Into<FlexParams>,
) {
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<Box<dyn Widget>>,
child: WidgetPod<dyn Widget>,
params: impl Into<FlexParams>,
) {
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<WidgetMut<'t, Box<dyn Widget>>> {
) -> Option<WidgetMut<'t, dyn Widget>> {
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<CrossAxisAlignment> for FlexParams {
}
impl Child {
fn widget_mut(&mut self) -> Option<&mut WidgetPod<Box<dyn Widget>>> {
fn widget_mut(&mut self) -> Option<&mut WidgetPod<dyn Widget>> {
match self {
Self::Fixed { widget, .. } | Self::Flex { widget, .. } => Some(widget),
_ => None,
}
}
fn widget(&self) -> Option<&WidgetPod<Box<dyn Widget>>> {
fn widget(&self) -> Option<&WidgetPod<dyn Widget>> {
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<Box<dyn Widget>>) -> Child {
fn new_flex_child(params: FlexParams, widget: WidgetPod<dyn Widget>) -> Child {
if let Some(flex) = params.flex {
if flex.is_normal() && flex > 0.0 {
Child::Flex {

View File

@ -21,7 +21,7 @@ pub struct Grid {
}
struct Child {
widget: WidgetPod<Box<dyn Widget>>,
widget: WidgetPod<dyn Widget>,
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<Box<dyn Widget>>,
params: GridParams,
) -> Self {
pub fn with_child_pod(mut self, widget: WidgetPod<dyn Widget>, 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<Box<dyn Widget>>> {
fn widget_mut(&mut self) -> Option<&mut WidgetPod<dyn Widget>> {
Some(&mut self.widget)
}
fn widget(&self) -> Option<&WidgetPod<Box<dyn Widget>>> {
fn widget(&self) -> Option<&WidgetPod<dyn Widget>> {
Some(&self.widget)
}
@ -97,7 +93,7 @@ impl Child {
}
}
fn new_grid_child(params: GridParams, widget: WidgetPod<Box<dyn Widget>>) -> Child {
fn new_grid_child(params: GridParams, widget: WidgetPod<dyn Widget>) -> 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<Box<dyn Widget>> = WidgetPod::new(Box::new(child));
let child_pod: WidgetPod<dyn Widget> = 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<Box<dyn Widget>> = WidgetPod::new_with_id(Box::new(child), id);
let child_pod: WidgetPod<dyn Widget> = 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<Box<dyn Widget>>,
widget: WidgetPod<dyn Widget>,
params: GridParams,
) {
let child = new_grid_child(params, widget);
@ -181,13 +177,13 @@ impl Grid {
child: impl Widget,
params: impl Into<GridParams>,
) {
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<Box<dyn Widget>>,
child: WidgetPod<dyn Widget>,
params: impl Into<GridParams>,
) {
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<WidgetMut<'t, Box<dyn Widget>>> {
) -> Option<WidgetMut<'t, dyn Widget>> {
let child = this.widget.children[idx].widget_mut()?;
Some(this.ctx.get_mut(child))
}

View File

@ -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<W: Widget> {
pub struct Portal<W: Widget + ?Sized> {
child: WidgetPod<W>,
// 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<W: Widget> Portal<W> {
pub fn new(child: W) -> Self {
Self::new_pod(WidgetPod::new(child))
}
}
impl<W: Widget + ?Sized> Portal<W> {
pub fn new_pod(child: WidgetPod<W>) -> Self {
Self {
child,
@ -127,7 +130,7 @@ fn compute_pan_range(mut viewport: Range<f64>, target: Range<f64>) -> Range<f64>
viewport
}
impl<W: Widget> Portal<W> {
impl<W: Widget + ?Sized> Portal<W> {
// 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<W: Widget> Portal<W> {
}
// --- MARK: WIDGETMUT ---
impl<W: Widget> Portal<W> {
impl<W: Widget + FromDynWidget + ?Sized> Portal<W> {
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<W: Widget> Portal<W> {
}
// --- MARK: IMPL WIDGET ---
impl<W: Widget> Widget for Portal<W> {
impl<W: Widget + FromDynWidget + ?Sized> Widget for Portal<W> {
fn on_pointer_event(&mut self, ctx: &mut EventCtx, event: &PointerEvent) {
const SCROLLING_SPEED: f64 = 10.0;

View File

@ -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<W> {
pub struct RootWidget<W: ?Sized> {
pub(crate) pod: WidgetPod<W>,
}
@ -25,19 +25,21 @@ impl<W: Widget> RootWidget<W> {
pod: WidgetPod::new(widget),
}
}
}
impl<W: Widget + FromDynWidget + ?Sized> RootWidget<W> {
pub fn from_pod(pod: WidgetPod<W>) -> Self {
Self { pod }
}
}
impl<W: Widget> RootWidget<W> {
impl<W: Widget + FromDynWidget + ?Sized> RootWidget<W> {
pub fn child_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> WidgetMut<'t, W> {
this.ctx.get_mut(&mut this.widget.pod)
}
}
impl<W: Widget> Widget for RootWidget<W> {
impl<W: Widget + FromDynWidget + ?Sized> Widget for RootWidget<W> {
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) {}

View File

@ -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<WidgetPod<Box<dyn Widget>>>,
child: Option<WidgetPod<dyn Widget>>,
width: Option<f64>,
height: Option<f64>,
background: Option<Brush>,
@ -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<Box<dyn Widget>>) -> Self {
pub fn new_pod(child: WidgetPod<dyn Widget>) -> 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<WidgetMut<'t, Box<dyn Widget>>> {
pub fn child_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> Option<WidgetMut<'t, dyn Widget>> {
let child = this.widget.child.as_mut()?;
Some(this.ctx.get_mut(child))
}

View File

@ -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<Box<dyn Widget>>,
child2: WidgetPod<Box<dyn Widget>>,
child1: WidgetPod<dyn Widget>,
child2: WidgetPod<dyn Widget>,
}
// --- 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(),
}
}

View File

@ -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<SizedBox> {
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);

View File

@ -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<Self>) -> Box<dyn Widget>;
fn as_dyn(&self) -> &dyn Widget;
fn as_mut_dyn(&mut self) -> &mut dyn Widget;
}
impl<T: Widget> AsDynWidget for T {
fn as_box_dyn(self: Box<Self>) -> Box<dyn Widget> {
self
}
fn as_dyn(&self) -> &dyn Widget {
self as &dyn Widget
}
@ -67,6 +71,34 @@ impl<T: Widget> 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<T: Widget> 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<dyn Widget> {
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<String> {
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<WidgetRef<'c, dyn Widget>> {
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()
}
}

View File

@ -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<W: Widget> Drop for WidgetMut<'_, W> {
impl<W: Widget + ?Sized> 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<W: Widget> Drop for WidgetMut<'_, W> {
}
}
impl<W: Widget> WidgetMut<'_, W> {
impl<W: Widget + ?Sized> 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<W: Widget> WidgetMut<'_, W> {
pub fn set_transform(&mut self, transform: Affine) {
self.ctx.set_transform(transform);
}
}
impl WidgetMut<'_, Box<dyn Widget>> {
/// Attempt to downcast to `WidgetMut` of concrete Widget type.
pub fn try_downcast<W2: Widget>(&mut self) -> Option<WidgetMut<'_, W2>> {
pub fn try_downcast<W2: Widget + FromDynWidget + ?Sized>(
&mut self,
) -> Option<WidgetMut<'_, W2>> {
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<dyn Widget>> {
///
/// Panics if the downcast fails, with an error message that shows the
/// discrepancy between the expected and actual types.
pub fn downcast<W2: Widget>(&mut self) -> WidgetMut<'_, W2> {
pub fn downcast<W2: Widget + FromDynWidget + ?Sized>(&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,

View File

@ -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<W> {
pub struct WidgetPod<W: ?Sized> {
id: WidgetId,
inner: WidgetPodInner<W>,
}
@ -20,12 +20,12 @@ pub struct WidgetPod<W> {
// through context methods where they already have access to the arena.
// Implementing that requires solving non-trivial design questions.
pub(crate) struct CreateWidget<W> {
pub(crate) widget: W,
pub(crate) struct CreateWidget<W: ?Sized> {
pub(crate) widget: Box<W>,
pub(crate) transform: Affine,
}
enum WidgetPodInner<W> {
enum WidgetPodInner<W: ?Sized> {
Create(CreateWidget<W>),
Inserted,
}
@ -42,15 +42,17 @@ impl<W: Widget> WidgetPod<W> {
/// 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<W: Widget + ?Sized> WidgetPod<W> {
/// 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<W>, 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<W>, id: WidgetId, transform: Affine) -> Self {
Self {
id,
inner: WidgetPodInner::Create(CreateWidget {
@ -59,7 +61,6 @@ impl<W: Widget> WidgetPod<W> {
}),
}
}
pub(crate) fn incomplete(&self) -> bool {
matches!(self.inner, WidgetPodInner::Create(_))
}
@ -75,17 +76,23 @@ impl<W: Widget> WidgetPod<W> {
pub fn id(&self) -> WidgetId {
self.id
}
}
impl<W: Widget + 'static> WidgetPod<W> {
/// 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<Box<dyn Widget>> {
/// Convert a `WidgetPod` pointing to a widget of a specific concrete type
/// `WidgetPod` pointing to a `dyn Widget`.
pub fn erased(self) -> WidgetPod<dyn Widget> {
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,
}),
}
}
}

View File

@ -12,7 +12,7 @@ use smallvec::SmallVec;
use tracing::trace_span;
struct Child {
widget: WidgetPod<Box<dyn Widget>>,
widget: WidgetPod<dyn Widget>,
alignment: ChildAlignment,
}
@ -163,7 +163,7 @@ impl From<Alignment> for ChildAlignment {
}
impl Child {
fn new(widget: WidgetPod<Box<dyn Widget>>, alignment: ChildAlignment) -> Self {
fn new(widget: WidgetPod<dyn Widget>, 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<ChildAlignment>) -> 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<ChildAlignment>,
) -> 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<Box<dyn Widget>>,
child: WidgetPod<dyn Widget>,
alignment: impl Into<ChildAlignment>,
) -> Self {
let child = Child::new(child, alignment.into());
@ -226,7 +226,7 @@ impl ZStack {
child: impl Widget,
alignment: impl Into<ChildAlignment>,
) {
let child_pod: WidgetPod<Box<dyn Widget>> = WidgetPod::new(Box::new(child));
let child_pod: WidgetPod<dyn Widget> = WidgetPod::new(child).erased();
Self::insert_child_pod(this, child_pod, alignment);
}
@ -239,14 +239,14 @@ impl ZStack {
id: WidgetId,
alignment: impl Into<ChildAlignment>,
) {
let child_pod: WidgetPod<Box<dyn Widget>> = WidgetPod::new_with_id(Box::new(child), id);
let child_pod: WidgetPod<dyn Widget> = 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<Box<dyn Widget>>,
widget: WidgetPod<dyn Widget>,
alignment: impl Into<ChildAlignment>,
) {
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<WidgetMut<'t, Box<dyn Widget>>> {
) -> Option<WidgetMut<'t, dyn Widget>> {
let child = &mut this.widget.children[idx].widget;
Some(this.ctx.get_mut(child))
}

View File

@ -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<State, Action = ()> =
dyn AnyView<State, Action, ViewCtx, Pod<DynWidget>> + Send + Sync;
impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for Pod<DynWidget> {
impl<W: Widget + FromDynWidget + ?Sized> SuperElement<Pod<W>, ViewCtx> for Pod<DynWidget> {
fn upcast(ctx: &mut ViewCtx, child: Pod<W>) -> Self {
ctx.new_pod(DynWidget {
inner: child.boxed_widget_pod(),
inner: child.erased_widget_pod(),
})
}
@ -46,26 +46,21 @@ impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for Pod<DynWidget> {
}
}
impl<W: Widget> AnyElement<Pod<W>, ViewCtx> for Pod<DynWidget> {
impl<W: Widget + FromDynWidget + ?Sized> AnyElement<Pod<W>, ViewCtx> for Pod<DynWidget> {
fn replace_inner(mut this: Self::Mut<'_>, child: Pod<W>) -> 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<Box<dyn Widget>>` doesn't expose this possibility.
#[allow(unnameable_types)] // This is an implementation detail of `AnyWidgetView`
pub struct DynWidget {
inner: WidgetPod<Box<dyn Widget>>,
inner: WidgetPod<dyn Widget>,
}
impl DynWidget {
pub(crate) fn replace_inner(
this: &mut WidgetMut<'_, Self>,
widget: WidgetPod<Box<dyn Widget>>,
) {
pub(crate) fn replace_inner(this: &mut WidgetMut<'_, Self>, widget: WidgetPod<dyn Widget>) {
let old_widget = std::mem::replace(&mut this.widget.inner, widget);
this.ctx.remove_child(old_widget);
}

View File

@ -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<W: Widget> {
pub widget: W,
pub struct Pod<W: Widget + FromDynWidget + ?Sized> {
pub widget: Box<W>,
pub id: WidgetId,
/// The transform the widget will be created with.
///
@ -198,36 +198,39 @@ pub struct Pod<W: Widget> {
pub transform: Affine,
}
impl<W: Widget> Pod<W> {
impl<W: Widget + FromDynWidget> Pod<W> {
fn new(widget: W) -> Self {
Self {
widget,
widget: Box::new(widget),
id: WidgetId::next(),
transform: Default::default(),
}
}
}
impl<W: Widget + FromDynWidget + ?Sized> Pod<W> {
fn erased(self) -> Pod<dyn Widget> {
Pod {
widget: self.widget.as_box_dyn(),
id: self.id,
transform: self.transform,
}
}
fn into_widget_pod(self) -> WidgetPod<W> {
WidgetPod::new_with_id_and_transform(self.widget, self.id, self.transform)
}
fn boxed_widget_pod(self) -> WidgetPod<Box<dyn Widget>> {
WidgetPod::new_with_id_and_transform(Box::new(self.widget), self.id, self.transform)
}
fn boxed(self) -> Pod<Box<dyn Widget>> {
Pod {
widget: Box::new(self.widget),
id: self.id,
transform: self.transform,
}
fn erased_widget_pod(self) -> WidgetPod<dyn Widget> {
WidgetPod::new_with_id_and_transform(self.widget, self.id, self.transform).erased()
}
}
impl<W: Widget> ViewElement for Pod<W> {
impl<W: Widget + FromDynWidget + ?Sized> ViewElement for Pod<W> {
type Mut<'a> = WidgetMut<'a, W>;
}
impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for Pod<Box<dyn Widget>> {
impl<W: Widget + FromDynWidget + ?Sized> SuperElement<Pod<W>, ViewCtx> for Pod<dyn Widget> {
fn upcast(_: &mut ViewCtx, child: Pod<W>) -> Self {
child.boxed()
child.erased()
}
fn with_downcast_val<R>(
@ -243,7 +246,7 @@ impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for Pod<Box<dyn Widget>> {
pub trait WidgetView<State, Action = ()>:
View<State, Action, ViewCtx, Element = Pod<Self::Widget>> + Send + Sync
{
type Widget: Widget;
type Widget: Widget + FromDynWidget + ?Sized;
/// Returns a boxed type erased [`AnyWidgetView`]
///
@ -281,7 +284,7 @@ pub trait WidgetView<State, Action = ()>:
impl<V, State, Action, W> WidgetView<State, Action> for V
where
V: View<State, Action, ViewCtx, Element = Pod<W>> + 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<W: Widget>(&mut self, widget: W) -> Pod<W> {
pub fn new_pod<W: Widget + FromDynWidget>(&mut self, widget: W) -> Pod<W> {
Pod::new(widget)
}
pub fn with_leaf_action_widget<E: Widget>(
pub fn with_leaf_action_widget<W: Widget + FromDynWidget + ?Sized>(
&mut self,
f: impl FnOnce(&mut Self) -> Pod<E>,
) -> (Pod<E>, ()) {
f: impl FnOnce(&mut Self) -> Pod<W>,
) -> (Pod<W>, ()) {
(self.with_action_widget(f), ())
}
pub fn with_action_widget<E: Widget>(&mut self, f: impl FnOnce(&mut Self) -> Pod<E>) -> Pod<E> {
pub fn with_action_widget<W: Widget + FromDynWidget + ?Sized>(
&mut self,
f: impl FnOnce(&mut Self) -> Pod<W>,
) -> Pod<W> {
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<E: Widget>(&mut self, widget: WidgetMut<E>) {
pub fn teardown_leaf<W: Widget + FromDynWidget + ?Sized>(&mut self, widget: WidgetMut<W>) {
self.widget_map.remove(&widget.ctx.widget_id());
}

View File

@ -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<A>,
@ -143,11 +143,21 @@ impl<
}
impl crate::core::one_of::PhantomElementCtx for ViewCtx {
type PhantomElement = Pod<Box<dyn Widget>>;
type PhantomElement = Pod<dyn Widget>;
}
#[allow(unnameable_types)] // reason: Implementation detail, public because of trait visibility rules
pub enum OneOfWidget<A, B, C, D, E, F, G, H, I> {
pub enum OneOfWidget<
A: ?Sized,
B: ?Sized,
C: ?Sized,
D: ?Sized,
E: ?Sized,
F: ?Sized,
G: ?Sized,
H: ?Sized,
I: ?Sized,
> {
A(WidgetPod<A>),
B(WidgetPod<B>),
C(WidgetPod<C>),
@ -160,15 +170,15 @@ pub enum OneOfWidget<A, B, C, D, E, F, G, H, I> {
}
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<A, B, C, D, E, F, G, H, I>
{
fn on_pointer_event(&mut self, _ctx: &mut EventCtx, _event: &PointerEvent) {}

View File

@ -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<Box<dyn Widget>>, FlexParams),
Child(Pod<dyn Widget>, FlexParams),
FixedSpacer(f64),
FlexSpacer(f64),
}
@ -221,9 +221,9 @@ impl SuperElement<Self, ViewCtx> for FlexElement {
}
}
impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for FlexElement {
impl<W: Widget + FromDynWidget + ?Sized> SuperElement<Pod<W>, ViewCtx> for FlexElement {
fn upcast(_: &mut ViewCtx, child: Pod<W>) -> Self {
Self::Child(child.boxed(), FlexParams::default())
Self::Child(child.erased(), FlexParams::default())
}
fn with_downcast_val<R>(
@ -248,7 +248,7 @@ impl ElementSplice<FlexElement> 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<FlexElement> 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 {

View File

@ -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<Self, ViewCtx> for GridElement {
}
}
impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for GridElement {
impl<W: Widget + FromDynWidget + ?Sized> SuperElement<Pod<W>, ViewCtx> for GridElement {
fn upcast(_: &mut ViewCtx, child: Pod<W>) -> 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<R>(
@ -186,7 +186,7 @@ impl ElementSplice<GridElement> 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<GridElement> 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<State, Action>: WidgetView<State, Action> {
impl<State, Action, V: WidgetView<State, Action>> GridExt<State, Action> for V {}
pub enum GridElement {
Child(Pod<Box<dyn Widget>>, GridParams),
Child(Pod<dyn Widget>, 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(

View File

@ -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)

View File

@ -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<Box<dyn Widget>>,
widget: Pod<dyn Widget>,
alignment: ChildAlignment,
}
@ -243,7 +243,7 @@ pub struct ZStackElementMut<'w> {
}
impl ZStackElement {
fn new(widget: Pod<Box<dyn Widget>>, alignment: ChildAlignment) -> Self {
fn new(widget: Pod<dyn Widget>, alignment: ChildAlignment) -> Self {
Self { widget, alignment }
}
}
@ -273,9 +273,9 @@ impl SuperElement<Self, ViewCtx> for ZStackElement {
}
}
impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for ZStackElement {
impl<W: Widget + FromDynWidget + ?Sized> SuperElement<Pod<W>, ViewCtx> for ZStackElement {
fn upcast(_: &mut ViewCtx, child: Pod<W>) -> Self {
Self::new(child.boxed(), ChildAlignment::ParentAligned)
Self::new(child.erased(), ChildAlignment::ParentAligned)
}
fn with_downcast_val<R>(
@ -331,7 +331,7 @@ impl ElementSplice<ZStackElement> 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<ZStackElement> 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;

View File

@ -25,7 +25,7 @@ fn main() {
trait Widget: 'static + Any {
fn as_mut_any(&mut self) -> &mut dyn Any;
}
struct WidgetPod<W: Widget> {
struct WidgetBox<W: Widget> {
widget: W,
}
struct WidgetMut<'a, W: Widget> {
@ -41,18 +41,18 @@ impl Widget for Box<dyn Widget> {
// Hmm, this implementation can't exist in `xilem` if `xilem_core` and/or `masonry` are a different crate
// due to the orphan rules...
impl<W: Widget> ViewElement for WidgetPod<W> {
impl<W: Widget> ViewElement for WidgetBox<W> {
type Mut<'a> = WidgetMut<'a, W>;
}
impl ViewMarker for Button {}
impl<State, Action> View<State, Action, ViewCtx> for Button {
type Element = WidgetPod<ButtonWidget>;
type Element = WidgetBox<ButtonWidget>;
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<W: Widget> SuperElement<WidgetPod<W>, ViewCtx> for WidgetPod<Box<dyn Widget>> {
fn upcast(_ctx: &mut ViewCtx, child: WidgetPod<W>) -> Self {
WidgetPod {
impl<W: Widget> SuperElement<WidgetBox<W>, ViewCtx> for WidgetBox<Box<dyn Widget>> {
fn upcast(_ctx: &mut ViewCtx, child: WidgetBox<W>) -> Self {
WidgetBox {
widget: Box::new(child.widget),
}
}
fn with_downcast_val<R>(
this: Self::Mut<'_>,
f: impl FnOnce(<WidgetPod<W> as ViewElement>::Mut<'_>) -> R,
f: impl FnOnce(<WidgetBox<W> 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<State, Action = ()>:
View<State, Action, ViewCtx, Element = WidgetPod<Self::Widget>> + Send + Sync
View<State, Action, ViewCtx, Element = WidgetBox<Self::Widget>> + Send + Sync
{
type Widget: Widget + Send + Sync;
}
impl<V, State, Action, W> WidgetView<State, Action> for V
where
V: View<State, Action, ViewCtx, Element = WidgetPod<W>> + Send + Sync,
V: View<State, Action, ViewCtx, Element = WidgetBox<W>> + Send + Sync,
W: Widget + Send + Sync,
{
type Widget = W;