Reserve ability to create Pod to ViewCtx (#597)

This is another intermediary PR for the "restrict widget creation to
Mutate pass" feature.

The basic idea is that, in the near future, it will be impossible to
create a WidgetPod without a handle to the arena. In my current WIP
code, that handle is passed through ViewCtx. Therefore, this PR changes
all the sites in xilem that create a Pod and has them use a ViewCtx
method instead.

I've tested most xilem examples and they all worked (except for
variable_clock, which currently panics in the last main commit).
This commit is contained in:
Olivier FAURE 2024-09-18 22:53:31 +02:00 committed by GitHub
parent 6c91b74e97
commit 25218291d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 94 additions and 93 deletions

View File

@ -25,12 +25,12 @@ use crate::{Pod, ViewCtx};
pub type AnyWidgetView<State, Action = ()> =
dyn AnyView<State, Action, ViewCtx, Pod<DynWidget>> + Send + Sync;
impl<W: Widget> SuperElement<Pod<W>> for Pod<DynWidget> {
fn upcast(child: Pod<W>) -> Self {
WidgetPod::new(DynWidget {
inner: child.inner.boxed(),
impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for Pod<DynWidget> {
fn upcast(ctx: &mut ViewCtx, child: Pod<W>) -> Self {
let boxed_pod = ctx.boxed_pod(child);
ctx.new_pod(DynWidget {
inner: boxed_pod.inner.boxed(),
})
.into()
}
fn with_downcast_val<R>(
@ -47,7 +47,7 @@ impl<W: Widget> SuperElement<Pod<W>> for Pod<DynWidget> {
}
}
impl<W: Widget> AnyElement<Pod<W>> for Pod<DynWidget> {
impl<W: Widget> 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.inner.boxed());
this

View File

@ -156,26 +156,13 @@ pub struct Pod<W: Widget> {
pub inner: WidgetPod<W>,
}
impl<W: Widget> Pod<W> {
/// Create a new `Pod` for `inner`.
pub fn new(inner: W) -> Self {
Self::from(WidgetPod::new(inner))
}
}
impl<W: Widget> From<WidgetPod<W>> for Pod<W> {
fn from(inner: WidgetPod<W>) -> Self {
Pod { inner }
}
}
impl<W: Widget> ViewElement for Pod<W> {
type Mut<'a> = WidgetMut<'a, W>;
}
impl<W: Widget> SuperElement<Pod<W>> for Pod<Box<dyn Widget>> {
fn upcast(child: Pod<W>) -> Self {
child.inner.boxed().into()
impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for Pod<Box<dyn Widget>> {
fn upcast(ctx: &mut ViewCtx, child: Pod<W>) -> Self {
ctx.boxed_pod(child)
}
fn with_downcast_val<R>(
@ -274,6 +261,18 @@ impl ViewPathTracker for ViewCtx {
}
impl ViewCtx {
pub fn new_pod<W: Widget>(&mut self, widget: W) -> Pod<W> {
Pod {
inner: WidgetPod::new(widget),
}
}
pub fn boxed_pod<W: Widget>(&mut self, pod: Pod<W>) -> Pod<Box<dyn Widget>> {
Pod {
inner: pod.inner.boxed(),
}
}
pub fn mark_changed(&mut self) {
if cfg!(debug_assertions) {
self.view_tree_changed = true;

View File

@ -120,6 +120,7 @@ impl<
}
}
fn upcast_one_of_element(
&mut self,
elem: xilem_core::one_of::OneOf<
Pod<A>,
Pod<B>,
@ -133,15 +134,15 @@ impl<
>,
) -> Self::OneOfElement {
match elem {
xilem_core::one_of::OneOf::A(w) => Pod::new(OneOfWidget::A(w.inner)),
xilem_core::one_of::OneOf::B(w) => Pod::new(OneOfWidget::B(w.inner)),
xilem_core::one_of::OneOf::C(w) => Pod::new(OneOfWidget::C(w.inner)),
xilem_core::one_of::OneOf::D(w) => Pod::new(OneOfWidget::D(w.inner)),
xilem_core::one_of::OneOf::E(w) => Pod::new(OneOfWidget::E(w.inner)),
xilem_core::one_of::OneOf::F(w) => Pod::new(OneOfWidget::F(w.inner)),
xilem_core::one_of::OneOf::G(w) => Pod::new(OneOfWidget::G(w.inner)),
xilem_core::one_of::OneOf::H(w) => Pod::new(OneOfWidget::H(w.inner)),
xilem_core::one_of::OneOf::I(w) => Pod::new(OneOfWidget::I(w.inner)),
xilem_core::one_of::OneOf::A(w) => self.new_pod(OneOfWidget::A(w.inner)),
xilem_core::one_of::OneOf::B(w) => self.new_pod(OneOfWidget::B(w.inner)),
xilem_core::one_of::OneOf::C(w) => self.new_pod(OneOfWidget::C(w.inner)),
xilem_core::one_of::OneOf::D(w) => self.new_pod(OneOfWidget::D(w.inner)),
xilem_core::one_of::OneOf::E(w) => self.new_pod(OneOfWidget::E(w.inner)),
xilem_core::one_of::OneOf::F(w) => self.new_pod(OneOfWidget::F(w.inner)),
xilem_core::one_of::OneOf::G(w) => self.new_pod(OneOfWidget::G(w.inner)),
xilem_core::one_of::OneOf::H(w) => self.new_pod(OneOfWidget::H(w.inner)),
xilem_core::one_of::OneOf::I(w) => self.new_pod(OneOfWidget::I(w.inner)),
}
}

View File

@ -50,7 +50,7 @@ where
type ViewState = ();
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
ctx.with_leaf_action_widget(|_| Pod::new(widget::Button::new(self.label.clone())))
ctx.with_leaf_action_widget(|ctx| ctx.new_pod(widget::Button::new(self.label.clone())))
}
fn rebuild<'el>(

View File

@ -36,8 +36,8 @@ where
type ViewState = ();
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
ctx.with_leaf_action_widget(|_| {
Pod::new(masonry::widget::Checkbox::new(
ctx.with_leaf_action_widget(|ctx| {
ctx.new_pod(masonry::widget::Checkbox::new(
self.checked,
self.label.clone(),
))

View File

@ -115,7 +115,7 @@ where
FlexElement::FlexSpacer(flex) => widget.with_flex_spacer(flex),
}
}
(Pod::new(widget), seq_state)
(ctx.new_pod(widget), seq_state)
}
fn rebuild<'el>(
@ -209,8 +209,8 @@ impl ViewElement for FlexElement {
type Mut<'w> = FlexElementMut<'w>;
}
impl SuperElement<FlexElement> for FlexElement {
fn upcast(child: FlexElement) -> Self {
impl SuperElement<FlexElement, ViewCtx> for FlexElement {
fn upcast(_ctx: &mut ViewCtx, child: FlexElement) -> Self {
child
}
@ -230,9 +230,9 @@ impl SuperElement<FlexElement> for FlexElement {
}
}
impl<W: Widget> SuperElement<Pod<W>> for FlexElement {
fn upcast(child: Pod<W>) -> Self {
FlexElement::Child(child.inner.boxed().into(), FlexParams::default())
impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for FlexElement {
fn upcast(ctx: &mut ViewCtx, child: Pod<W>) -> Self {
FlexElement::Child(ctx.boxed_pod(child), FlexParams::default())
}
fn with_downcast_val<R>(
@ -450,10 +450,7 @@ where
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
let (pod, state) = self.view.build(ctx);
(
FlexElement::Child(pod.inner.boxed().into(), self.params),
state,
)
(FlexElement::Child(ctx.boxed_pod(pod), self.params), state)
}
fn rebuild<'el>(

View File

@ -74,7 +74,7 @@ where
GridElement::Child(child, params) => widget.with_child_pod(child.inner, params),
}
}
(Pod::new(widget), seq_state)
(ctx.new_pod(widget), seq_state)
}
fn rebuild<'el>(
@ -133,8 +133,8 @@ impl ViewElement for GridElement {
}
// Used to allow the item to be used as a generic item in ViewSequence.
impl SuperElement<GridElement> for GridElement {
fn upcast(child: GridElement) -> Self {
impl SuperElement<GridElement, ViewCtx> for GridElement {
fn upcast(_ctx: &mut ViewCtx, child: GridElement) -> Self {
child
}
@ -154,14 +154,14 @@ impl SuperElement<GridElement> for GridElement {
}
}
impl<W: Widget> SuperElement<Pod<W>> for GridElement {
fn upcast(child: Pod<W>) -> Self {
impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for GridElement {
fn upcast(ctx: &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.
GridElement::Child(child.inner.boxed().into(), GridParams::new(1, 1, 1, 1))
GridElement::Child(ctx.boxed_pod(child), GridParams::new(1, 1, 1, 1))
}
fn with_downcast_val<R>(
@ -362,12 +362,9 @@ where
type ViewState = V::ViewState;
fn build(&self, cx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
let (pod, state) = self.view.build(cx);
(
GridElement::Child(pod.inner.boxed().into(), self.params),
state,
)
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
let (pod, state) = self.view.build(ctx);
(GridElement::Child(ctx.boxed_pod(pod), self.params), state)
}
fn rebuild<'el>(

View File

@ -49,8 +49,8 @@ impl<State, Action> View<State, Action, ViewCtx> for Image {
type Element = Pod<widget::Image>;
type ViewState = ();
fn build(&self, _: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
(Pod::new(widget::Image::new(self.image.clone())), ())
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
(ctx.new_pod(widget::Image::new(self.image.clone())), ())
}
fn rebuild<'el>(

View File

@ -48,8 +48,8 @@ impl<State, Action> View<State, Action, ViewCtx> for Label {
type Element = Pod<widget::Label>;
type ViewState = ();
fn build(&self, _ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
let widget_pod = Pod::new(
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
let widget_pod = ctx.new_pod(
widget::Label::new(self.label.clone())
.with_text_brush(self.text_brush.clone())
.with_text_alignment(self.alignment)

View File

@ -40,7 +40,7 @@ where
// The Portal `View` doesn't get any messages directly (yet - scroll events?), so doesn't need to
// use ctx.with_id.
let (child, child_state) = self.child.build(ctx);
let widget_pod = Pod::new(widget::Portal::new_pod(child.inner));
let widget_pod = ctx.new_pod(widget::Portal::new_pod(child.inner));
(widget_pod, child_state)
}

View File

@ -20,7 +20,9 @@ impl<State, Action> View<State, Action, ViewCtx> for ProgressBar {
type ViewState = ();
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
ctx.with_leaf_action_widget(|_| Pod::new(masonry::widget::ProgressBar::new(self.progress)))
ctx.with_leaf_action_widget(|ctx| {
ctx.new_pod(masonry::widget::ProgressBar::new(self.progress))
})
}
fn rebuild<'el>(

View File

@ -49,8 +49,8 @@ impl<State, Action> View<State, Action, ViewCtx> for Prose {
type Element = Pod<widget::Prose>;
type ViewState = ();
fn build(&self, _ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
let widget_pod = Pod::new(
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
let widget_pod = ctx.new_pod(
widget::Prose::new(self.content.clone())
.with_text_brush(self.text_brush.clone())
.with_text_alignment(self.alignment)

View File

@ -135,7 +135,7 @@ where
if let Some(border) = &self.border {
widget = widget.border(border.color, border.width);
}
(Pod::new(widget), child_state)
(ctx.new_pod(widget), child_state)
}
fn rebuild<'el>(

View File

@ -55,8 +55,8 @@ impl<State, Action> View<State, Action, ViewCtx> for Spinner {
type Element = Pod<widget::Spinner>;
type ViewState = ();
fn build(&self, _: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
(Pod::new(widget::Spinner::new()), ())
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
(ctx.new_pod(widget::Spinner::new()), ())
}
fn rebuild<'el>(

View File

@ -69,8 +69,8 @@ impl<State: 'static, Action: 'static> View<State, Action, ViewCtx> for Textbox<S
type ViewState = ();
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
ctx.with_leaf_action_widget(|_| {
Pod::new(
ctx.with_leaf_action_widget(|ctx| {
ctx.new_pod(
masonry::widget::Textbox::new(self.contents.clone())
.with_text_brush(self.text_brush.clone())
.with_text_alignment(self.alignment),

View File

@ -100,8 +100,8 @@ impl<State, Action> View<State, Action, ViewCtx> for VariableLabel {
type Element = Pod<widget::VariableLabel>;
type ViewState = ();
fn build(&self, _ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
let widget_pod = Pod::new(
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
let widget_pod = ctx.new_pod(
widget::VariableLabel::new(self.label.clone())
.with_text_brush(self.text_brush.clone())
.with_line_break_mode(widget::LineBreaking::WordWrap)

View File

@ -98,8 +98,8 @@ impl<V, State, Action> FileView<State, Action> for V where
type DynFileView<State, Action = ()> = Box<dyn AnyView<State, Action, ViewCtx, FsPath>>;
impl SuperElement<FsPath> for FsPath {
fn upcast(child: FsPath) -> Self {
impl SuperElement<FsPath, ViewCtx> for FsPath {
fn upcast(_ctx: &mut ViewCtx, child: FsPath) -> Self {
child
}
@ -112,7 +112,7 @@ impl SuperElement<FsPath> for FsPath {
}
}
impl AnyElement<FsPath> for FsPath {
impl AnyElement<FsPath, ViewCtx> for FsPath {
fn replace_inner(this: Self::Mut<'_>, child: FsPath) -> Self::Mut<'_> {
*this = child.0;
this

View File

@ -99,8 +99,8 @@ impl Widget for ButtonWidget {
}
}
impl<W: Widget> SuperElement<WidgetPod<W>> for WidgetPod<Box<dyn Widget>> {
fn upcast(child: WidgetPod<W>) -> Self {
impl<W: Widget> SuperElement<WidgetPod<W>, ViewCtx> for WidgetPod<Box<dyn Widget>> {
fn upcast(_ctx: &mut ViewCtx, child: WidgetPod<W>) -> Self {
WidgetPod {
widget: Box::new(child.widget),
}

View File

@ -66,7 +66,7 @@ pub trait AnyView<State, Action, Context, Element: ViewElement, Message = DynMes
impl<State, Action, Context, DynamicElement, Message, V>
AnyView<State, Action, Context, DynamicElement, Message> for V
where
DynamicElement: AnyElement<V::Element>,
DynamicElement: AnyElement<V::Element, Context>,
Context: ViewPathTracker,
V: View<State, Action, Context, Message> + 'static,
V::ViewState: 'static,
@ -80,7 +80,7 @@ where
let generation = 0;
let (element, view_state) = ctx.with_id(ViewId::new(generation), |ctx| self.build(ctx));
(
DynamicElement::upcast(element),
DynamicElement::upcast(ctx, element),
AnyViewState {
inner_state: Box::new(view_state),
generation,

View File

@ -42,12 +42,12 @@ pub type Mut<'el, E> = <E as ViewElement>::Mut<'el>;
///
/// [`AnyView`]: crate::AnyView
/// [`ViewSequence`]: crate::ViewSequence
pub trait SuperElement<Child>: ViewElement
pub trait SuperElement<Child, Context>: ViewElement
where
Child: ViewElement,
{
/// Convert from the child to this element type.
fn upcast(child: Child) -> Self;
fn upcast(ctx: &mut Context, child: Child) -> Self;
/// Perform a reborrowing downcast to the child reference type.
///
@ -75,7 +75,7 @@ where
}
/// An element which can be used for an [`AnyView`](crate::AnyView) containing `Child`.
pub trait AnyElement<Child>: SuperElement<Child>
pub trait AnyElement<Child, Context>: SuperElement<Child, Context>
where
Child: ViewElement,
{
@ -95,8 +95,8 @@ impl ViewElement for NoElement {
type Mut<'a> = ();
}
impl SuperElement<NoElement> for NoElement {
fn upcast(child: NoElement) -> Self {
impl<Context> SuperElement<NoElement, Context> for NoElement {
fn upcast(_ctx: &mut Context, child: NoElement) -> Self {
child
}

View File

@ -150,14 +150,14 @@ impl<State, Action, Context, V, Element, Message>
where
Context: ViewPathTracker,
V: View<State, Action, Context, Message> + ViewMarker,
Element: SuperElement<V::Element>,
Element: SuperElement<V::Element, Context>,
V::Element: ViewElement,
{
type SeqState = V::ViewState;
fn seq_build(&self, ctx: &mut Context, elements: &mut AppendVec<Element>) -> Self::SeqState {
let (element, view_state) = self.build(ctx);
elements.push(Element::upcast(element));
elements.push(Element::upcast(ctx, element));
view_state
}
fn seq_rebuild(

View File

@ -137,7 +137,10 @@ pub trait OneOfCtx<
fn with_downcast_i(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, I>));
/// Creates the wrapping element, this is used in `View::build` to wrap the inner view element variant
fn upcast_one_of_element(elem: OneOf<A, B, C, D, E, F, G, H, I>) -> Self::OneOfElement;
fn upcast_one_of_element(
&mut self,
elem: OneOf<A, B, C, D, E, F, G, H, I>,
) -> Self::OneOfElement;
/// When the variant of the inner view element has changed, the wrapping element needs to be updated, this is used in `View::rebuild`
fn update_one_of_element_mut(
@ -233,7 +236,7 @@ where
}
});
(
Context::upcast_one_of_element(element),
ctx.upcast_one_of_element(element),
OneOfState {
generation,
inner_state: state,

View File

@ -210,8 +210,8 @@ impl<const N: u32> View<(), Action, TestCtx> for OperationView<N> {
}
}
impl SuperElement<TestElement> for TestElement {
fn upcast(child: TestElement) -> Self {
impl SuperElement<TestElement, TestCtx> for TestElement {
fn upcast(_ctx: &mut TestCtx, child: TestElement) -> Self {
child
}
@ -224,7 +224,7 @@ impl SuperElement<TestElement> for TestElement {
}
}
impl AnyElement<TestElement> for TestElement {
impl AnyElement<TestElement, TestCtx> for TestElement {
fn replace_inner(this: Self::Mut<'_>, child: TestElement) -> Self::Mut<'_> {
assert_eq!(child.operations.len(), 1);
let Operation::Build(child_id) = child.operations.first().unwrap() else {

View File

@ -41,6 +41,7 @@ impl
type OneOfElement = TestElement;
fn upcast_one_of_element(
&mut self,
elem: OneOf<
TestElement,
TestElement,

View File

@ -269,8 +269,8 @@ impl<E: DomNode<P>, P: 'static> ViewElement for Pod<E, P> {
type Mut<'a> = PodMut<'a, E, P>;
}
impl<E: DomNode<P>, P: 'static> SuperElement<Pod<E, P>> for AnyPod {
fn upcast(child: Pod<E, P>) -> Self {
impl<E: DomNode<P>, P: 'static> SuperElement<Pod<E, P>, ViewCtx> for AnyPod {
fn upcast(_ctx: &mut ViewCtx, child: Pod<E, P>) -> Self {
Pod::into_dyn_node(child.node, child.props)
}
@ -284,7 +284,7 @@ impl<E: DomNode<P>, P: 'static> SuperElement<Pod<E, P>> for AnyPod {
}
}
impl<E: DomNode<P>, P: 'static> AnyElement<Pod<E, P>> for AnyPod {
impl<E: DomNode<P>, P: 'static> AnyElement<Pod<E, P>, ViewCtx> for AnyPod {
fn replace_inner(mut this: Self::Mut<'_>, child: Pod<E, P>) -> Self::Mut<'_> {
Pod::replace_inner(&mut this, child);
this

View File

@ -50,6 +50,7 @@ where
Pod<OneOf<N1, N2, N3, N4, N5, N6, N7, N8, N9>, OneOf<P1, P2, P3, P4, P5, P6, P7, P8, P9>>;
fn upcast_one_of_element(
&mut self,
elem: OneOf<
Pod<N1, P1>,
Pod<N2, P2>,