diff --git a/core/src/builtin_widgets.rs b/core/src/builtin_widgets.rs index 966de3dd3..b0a0a0716 100644 --- a/core/src/builtin_widgets.rs +++ b/core/src/builtin_widgets.rs @@ -185,69 +185,65 @@ impl FatObj { } } -impl WidgetBuilder for FatObj -where - T: Into, -{ +impl> WidgetBuilder for FatObj { fn build(self, ctx: &BuildCtx) -> WidgetId { let Self { host, builtin } = self; builtin.compose_with_host(host.into(), ctx).build(ctx) } } -impl, C> SingleWithChild for FatObj { - type Target = FatObj; - fn with_child(self, child: C, ctx: &BuildCtx) -> Self::Target { - let Self { host, builtin } = self; - let host = host.with_child(child, ctx); - FatObj { host, builtin } +impl SingleChild for FatObj {} +impl MultiChild for FatObj {} +impl ComposeChild for FatObj> { + type Child = T::Child; + + fn compose_child(this: State, child: Self::Child) -> Widget { + let this = this.into_inner_value(); + let Self { host, builtin } = this; + FnWidget::new(move |ctx| { + let this = host.with_child(child, ctx); + builtin.compose_with_host(this.into(), ctx) + }) + .into() } } -impl, C> MultiWithChild for FatObj { - type Target = FatObj; - fn with_child(self, child: C, ctx: &BuildCtx) -> Self::Target { - let Self { host, builtin } = self; - let host = host.with_child(child, ctx); - FatObj { host, builtin } +impl ComposeChild for FatObj { + type Child = T::Child; + + fn compose_child(this: State, child: Self::Child) -> Widget { + let this = this.into_inner_value(); + let Self { host, builtin } = this; + FnWidget::new(move |ctx| { + let this = host.with_child(child, ctx); + builtin.compose_with_host(this.into(), ctx) + }) + .into() } } -impl ComposeWithChild for FatObj -where - T: ComposeWithChild, -{ - type Target = FatObj; - fn with_child(self, child: C, ctx: &BuildCtx) -> Self::Target { +impl SingleParent for FatObj { + fn append_child(self, child: WidgetId, ctx: &mut BuildCtx) -> WidgetId { let Self { host, builtin } = self; - let host = host.with_child(child, ctx); - FatObj { host, builtin } + let p = host.append_child(child, ctx); + builtin.compose_with_host(p.into(), ctx).build(ctx) } } -impl SingleWithChild for Option> -where - T: SingleWithChild, - C: Into, - as SingleWithChild>::Target: Into, -{ - type Target = Widget; - fn with_child(self, c: C, ctx: &BuildCtx) -> Self::Target { - if let Some(this) = self { - this.with_child(c, ctx).into() - } else { - c.into() - } +impl MultiParent for FatObj { + fn append_children(self, children: Vec, ctx: &mut BuildCtx) -> WidgetId { + let Self { host, builtin } = self; + let host = host.append_children(children, ctx); + builtin.compose_with_host(host.into(), ctx).build(ctx) } } -impl ComposeWithChild for BuiltinObj -where - C: Into, -{ - type Target = Widget; - fn with_child(self, child: C, ctx: &BuildCtx) -> Self::Target { - self.compose_with_host(child.into(), ctx) +impl ComposeChild for BuiltinObj { + type Child = Widget; + + fn compose_child(this: State, child: Self::Child) -> Widget { + let this = this.into_inner_value(); + fn_widget! { this.compose_with_host(child, ctx!()) }.into() } } @@ -261,17 +257,3 @@ impl std::ops::DerefMut for FatObj { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.host } } - -impl BoxedSingleParent for FatObj -where - W: SingleChild + Into> + Into + 'static, -{ - fn into_parent(self: Box, ctx: &mut BuildCtx) -> WidgetId { self.build(ctx) } -} - -impl BoxMultiParent for FatObj -where - W: MultiChild + Into> + Into + 'static, -{ - fn into_parent(self: Box, ctx: &mut BuildCtx) -> WidgetId { self.build(ctx) } -} diff --git a/core/src/pipe.rs b/core/src/pipe.rs index 1cafb33ef..62ff7af32 100644 --- a/core/src/pipe.rs +++ b/core/src/pipe.rs @@ -6,6 +6,7 @@ use rxrust::{ use std::{ cell::{Cell, RefCell}, convert::Infallible, + ops::Range, rc::Rc, }; @@ -13,9 +14,12 @@ use crate::{ builtin_widgets::{key::AnyKey, Void}, context::{AppCtx, BuildCtx}, data_widget::attach_to_id, - prelude::{AnonymousData, DataWidget, Multi, MultiChild, SingleChild}, + prelude::{ + AnonymousData, BoxedSingleParent, DataWidget, Multi, MultiChild, MultiParent, SingleChild, + SingleParent, + }, ticker::FrameMsg, - widget::{QueryOrder, Widget, WidgetBuilder, WidgetId}, + widget::{QueryOrder, Widget, WidgetBuilder, WidgetId, WidgetTree}, window::WindowId, }; @@ -115,47 +119,94 @@ impl + 'static> WidgetBuilder for Pipe> { } } -impl Pipe { - pub(crate) fn into_only_parent( - self, - mut f: impl FnMut(R, &mut BuildCtx) -> WidgetId + 'static, - ctx: &mut BuildCtx, - ) -> WidgetId { +impl SingleParent for Pipe { + fn append_child(self, child: WidgetId, ctx: &mut BuildCtx) -> WidgetId { let (v, modifies) = self.unzip(); - let id = f(v, ctx); - let id_share = Rc::new(Cell::new(id)); - let handle = ctx.handle(); - let unsub = modifies - .sample(new_frame_sampler!(ctx)) - .subscribe(move |v| { - handle.with_ctx(|ctx| { - let id = id_share.get(); - let ctx = ctx.force_as_mut(); - let new_id = f(v, ctx); + let p = v.append_child(child, ctx); + let rg = half_to_close_interval(p..child, &ctx.tree.borrow()); + update_pipe_parent(rg, modifies, ctx, |new_p, old_p, ctx| { + let child = old_p.single_child(&ctx.tree.borrow().arena).unwrap(); + new_p.append_child(child, ctx) + }); + p + } +} - update_key_status_single(id, new_id, ctx); - { - let tree = ctx.tree.borrow_mut(); - let mut cursor = id.first_child(&tree.arena); - while let Some(c) = cursor { - cursor = c.next_sibling(&tree.arena); - ctx.append_child(new_id, c); - } - } +impl MultiParent for Pipe { + fn append_children(self, children: Vec, ctx: &mut BuildCtx) -> WidgetId { + // if children is empty, we can let the pipe parent as the whole subtree. + if children.is_empty() { + self.build(ctx) + } else { + let (v, modifies) = self.unzip(); + let first_child = children[0]; + let p = v.append_children(children, ctx); + let rg = half_to_close_interval(p..first_child, &ctx.tree.borrow()); + update_pipe_parent(rg, modifies, ctx, |new_p, old_p, ctx| { + let children = old_p.children(&ctx.tree.borrow().arena).collect::>(); + new_p.append_children(children, ctx) + }); + p + } + } +} - ctx.insert_after(id, new_id); - ctx.dispose_subtree(id); +impl SingleParent for Pipe> { + fn append_child(self, child: WidgetId, ctx: &mut BuildCtx) -> WidgetId { + self + .map(|p| -> Box { + if let Some(p) = p { + Box::new(p) + } else { + Box::new(Void) + } + }) + .append_child(child, ctx) + } +} - ctx.on_widget_mounted(new_id); - id_share.set(new_id); - ctx.mark_dirty(new_id); - }); - }); +fn half_to_close_interval(rg: Range, tree: &WidgetTree) -> Range { + rg.start..rg.end.parent(&tree.arena).unwrap() +} - attach_unsubscribe_guard(id, ctx.window().id(), unsub); +fn update_pipe_parent( + // The range of the pipe parent widget ids. + parent: Range, + // transplant the children of the old parent to the new widget. + modifies: BoxOp<'static, W, Infallible>, + ctx: &mut BuildCtx, + transplant: impl Fn(W, WidgetId, &mut BuildCtx) -> WidgetId + 'static, +) { + let id_share = Rc::new(RefCell::new(parent.clone())); + let handle = ctx.handle(); + let unsub = modifies + .sample(new_frame_sampler!(ctx)) + .subscribe(move |v| { + handle.with_ctx(|ctx| { + let ctx = ctx.force_as_mut(); + let rg = id_share.borrow().clone(); + let tree = ctx.tree.borrow_mut(); + + let first_child = rg.end.first_child(&tree.arena).unwrap(); + let p = transplant(v, rg.end, ctx.force_as_mut()); + let new_rg = half_to_close_interval(p..first_child, &tree); + + update_key_status_single(rg.start, new_rg.start, ctx); + + ctx.insert_after(rg.start, new_rg.start); + ctx.dispose_subtree(rg.start); + new_rg + .end + .ancestors(&tree.arena) + .take_while(|w| w != &new_rg.start) + .for_each(|p| ctx.on_widget_mounted(p)); + + ctx.mark_dirty(new_rg.start); + *id_share.borrow_mut() = new_rg; + }); + }); - id - } + attach_unsubscribe_guard(parent.start, ctx.window().id(), unsub); } impl Pipe> { diff --git a/core/src/state.rs b/core/src/state.rs index 77841625c..7bbc6cb31 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -15,7 +15,7 @@ pub use stateful::*; use crate::{ context::BuildCtx, - prelude::{AnonymousData, BoxMultiParent, BoxedSingleParent, MultiChild, SingleChild}, + prelude::{AnonymousData, DataWidget, MultiChild, SingleChild}, widget::{Compose, Render, WidgetBuilder, WidgetId}, }; @@ -162,18 +162,6 @@ impl Share for State { impl SingleChild for State {} impl MultiChild for State {} -impl BoxedSingleParent for State { - fn into_parent(self: Box, ctx: &mut BuildCtx) -> WidgetId { - ctx.alloc_widget((*self).into()) - } -} - -impl BoxMultiParent for State { - fn into_parent(self: Box, ctx: &mut BuildCtx) -> WidgetId { - ctx.alloc_widget((*self).into()) - } -} - impl From> for Box { #[inline] fn from(s: State) -> Self { @@ -205,6 +193,23 @@ impl State { State(UnsafeCell::new(InnerState::Data(StateData::new(value)))) } + /// Unwrap the state and return the inner value. + /// + /// # Panics + /// panic if the state is stateful or already attached data. + pub fn into_inner_value(self) -> W { + match self.0.into_inner() { + InnerState::Data(w) => { + let (w, slot) = w.into_inner(); + assert!(slot.is_none(), "already attached data"); + w + } + InnerState::Stateful(w) => w + .try_into_inner() + .unwrap_or_else(|_| panic!("can't convert stateful to value")), + } + } + pub fn into_readonly(self) -> Readonly { match self.0.into_inner() { InnerState::Data(w) => { diff --git a/core/src/state/stateful.rs b/core/src/state/stateful.rs index b6c0cbd25..91c3fcf73 100644 --- a/core/src/state/stateful.rs +++ b/core/src/state/stateful.rs @@ -190,8 +190,9 @@ impl Stateful { } pub(crate) fn try_into_inner(self) -> Result { - if Rc::strong_count(&self.inner) == 1 { - let inner = Rc::try_unwrap(self.inner).unwrap_or_else(|_| unreachable!()); + if Rc::strong_count(&self.inner) == 1 && unsafe { &*self.inner.slot_link.get() }.is_none() { + // SAFETY: `Rc::strong_count(&self.inner) == 1` guarantees unique access. + let inner = unsafe { Rc::try_unwrap(self.inner).unwrap_unchecked() }; Ok(inner.data.into_inner()) } else { Err(self) diff --git a/core/src/widget_children.rs b/core/src/widget_children.rs index 70109aa66..8e64321da 100644 --- a/core/src/widget_children.rs +++ b/core/src/widget_children.rs @@ -29,7 +29,7 @@ //! clone, this seems too strict and if `A` is not support clone, the compile //! error is too complex to diagnostic. -use crate::prelude::*; +use crate::{prelude::*, widget::WidgetBuilder}; mod compose_child_impl; mod multi_child_impl; mod single_child_impl; @@ -43,15 +43,21 @@ pub trait SingleChild {} /// A boxed render widget that support accept one child. pub trait BoxedSingleParent { - fn into_parent(self: Box, ctx: &mut BuildCtx) -> WidgetId; + fn boxed_append_child(self: Box, child: WidgetId, ctx: &mut BuildCtx) -> WidgetId; + fn boxed_build(self: Box, ctx: &mut BuildCtx) -> WidgetId; } /// Trait to tell Ribir a object that has multi children. pub trait MultiChild {} /// A boxed render widget that support accept multi children. -pub trait BoxMultiParent { - fn into_parent(self: Box, ctx: &mut BuildCtx) -> WidgetId; +pub trait BoxedMultiParent { + fn boxed_append_children( + self: Box, + children: Vec, + ctx: &mut BuildCtx, + ) -> WidgetId; + fn boxed_build(self: Box, ctx: &mut BuildCtx) -> WidgetId; } /// Trait mark widget can have one child and also have compose logic for widget @@ -66,44 +72,89 @@ pub trait ComposeChild: Sized { pub type WidgetOf = SinglePair; impl SingleChild for Box {} -impl MultiChild for Box {} +impl MultiChild for Box {} -impl RenderParent for Box { - fn into_render_parent(self, ctx: &mut BuildCtx) -> WidgetId { self.into_parent(ctx) } +impl WidgetBuilder for Box { + #[inline] + fn build(self, ctx: &BuildCtx) -> WidgetId { self.boxed_build(ctx.force_as_mut()) } } -impl RenderParent for Box { - fn into_render_parent(self, ctx: &mut BuildCtx) -> WidgetId { self.into_parent(ctx) } +impl SingleParent for Box { + #[inline] + fn append_child(self, child: WidgetId, ctx: &mut BuildCtx) -> WidgetId { + self.boxed_append_child(child, ctx) + } } -pub(crate) trait RenderParent { - fn into_render_parent(self, ctx: &mut BuildCtx) -> WidgetId; +impl WidgetBuilder for Box { + #[inline] + fn build(self, ctx: &BuildCtx) -> WidgetId { self.boxed_build(ctx.force_as_mut()) } } -impl>> RenderParent for T { +impl MultiParent for Box { #[inline] - fn into_render_parent(self, ctx: &mut BuildCtx) -> WidgetId { ctx.alloc_widget(self.into()) } + fn append_children(self, children: Vec, ctx: &mut BuildCtx) -> WidgetId { + self.boxed_append_children(children, ctx) + } +} + +pub(crate) trait SingleParent: WidgetBuilder { + fn append_child(self, child: WidgetId, ctx: &mut BuildCtx) -> WidgetId; +} + +pub(crate) trait MultiParent: WidgetBuilder { + fn append_children(self, children: Vec, ctx: &mut BuildCtx) -> WidgetId; } -impl BoxedSingleParent for W { - fn into_parent(self: Box, ctx: &mut BuildCtx) -> WidgetId { - ctx.alloc_widget(Box::new(*self)) +impl> + SingleChild + WidgetBuilder> SingleParent for T { + fn append_child(self, child: WidgetId, ctx: &mut BuildCtx) -> WidgetId { + let p = self.build(ctx); + ctx.append_child(p, child); + p } } -impl BoxMultiParent for W { - fn into_parent(self: Box, ctx: &mut BuildCtx) -> WidgetId { - ctx.alloc_widget(Box::new(*self)) +impl> + MultiChild + WidgetBuilder> MultiParent for T { + #[inline] + fn append_children(self, children: Vec, ctx: &mut BuildCtx) -> WidgetId { + let p = self.build(ctx); + for c in children { + ctx.append_child(p, c); + } + p } } +impl BoxedSingleParent for W { + #[inline] + fn boxed_append_child(self: Box, child: WidgetId, ctx: &mut BuildCtx) -> WidgetId { + (*self).append_child(child, ctx) + } + + #[inline] + fn boxed_build(self: Box, ctx: &mut BuildCtx) -> WidgetId { (*self).build(ctx) } +} + +impl BoxedMultiParent for W { + #[inline] + fn boxed_append_children( + self: Box, + children: Vec, + ctx: &mut BuildCtx, + ) -> WidgetId { + (*self).append_children(children, ctx) + } + + #[inline] + fn boxed_build(self: Box, ctx: &mut BuildCtx) -> WidgetId { (*self).build(ctx) } +} + #[cfg(test)] mod tests { use super::*; use crate::test_helper::*; use crate::widget::WidgetBuilder; use ribir_dev_helper::*; - use std::{cell::RefCell, rc::Rc}; #[test] fn compose_template_child() { diff --git a/core/src/widget_children/child_convert.rs b/core/src/widget_children/child_convert.rs index 71011ce06..a48e57901 100644 --- a/core/src/widget_children/child_convert.rs +++ b/core/src/widget_children/child_convert.rs @@ -4,7 +4,7 @@ use super::{ }; use crate::{ builtin_widgets::FatObj, - state::{State, StateFrom, }, + state::{State, StateFrom}, widget::*, }; @@ -68,28 +68,38 @@ where impl FromAnother, [(M1, M2); 0]> for SinglePair where W: FromAnother, - C: ChildFrom, + C: FromAnother, { #[inline] fn from_another(value: SinglePair) -> Self { let SinglePair { widget, child } = value; SinglePair { widget: W::from_another(widget), - child: C::child_from(child), + child: C::from_another(child), } } } -impl FromAnother, [(M1, M2); 1]> for SinglePair +impl FromAnother, [M; 1]> for SinglePair where - W: ChildFrom, - C: FromAnother, + W: FromAnother, { #[inline] - fn from_another(value: SinglePair) -> Self { + fn from_another(value: SinglePair) -> Self { + let SinglePair { widget, child } = value; + SinglePair { widget: W::child_from(widget), child } + } +} + +impl FromAnother, [M; 2]> for SinglePair +where + C: FromAnother, +{ + #[inline] + fn from_another(value: SinglePair) -> Self { let SinglePair { widget, child } = value; SinglePair { - widget: W::child_from(widget), + widget, child: C::from_another(child), } } diff --git a/core/src/widget_children/multi_child_impl.rs b/core/src/widget_children/multi_child_impl.rs index 4ac362203..c69b66be1 100644 --- a/core/src/widget_children/multi_child_impl.rs +++ b/core/src/widget_children/multi_child_impl.rs @@ -8,8 +8,8 @@ pub trait MultiWithChild { fn with_child(self, child: C, ctx: &BuildCtx) -> Self::Target; } -pub struct MultiPair { - pub parent: WidgetId, +pub struct MultiPair

{ + pub parent: P, pub children: Vec, } @@ -57,8 +57,6 @@ where } } - - impl FillVec for Pipe> where D: IntoIterator + 'static, @@ -69,38 +67,21 @@ where } } -trait MultiParent { - fn into_multi_parent(self, ctx: &mut BuildCtx) -> WidgetId; -} - -impl MultiParent for R { - #[inline] - fn into_multi_parent(self, ctx: &mut BuildCtx) -> WidgetId { self.into_render_parent(ctx) } -} - -impl MultiParent for Pipe { - #[inline] - fn into_multi_parent(self, ctx: &mut BuildCtx) -> WidgetId { - self.into_only_parent(RenderParent::into_render_parent, ctx) - } -} - -impl MultiWithChild for R +impl MultiWithChild for P where - R: MultiParent, + P: MultiChild, C: FillVec, { - type Target = MultiPair; + type Target = MultiPair

; fn with_child(self, child: C, ctx: &BuildCtx) -> Self::Target { - let parent = self.into_multi_parent(ctx.force_as_mut()); let mut children = vec![]; child.fill_vec(&mut children, ctx); - MultiPair { parent, children } + MultiPair { parent: self, children } } } -impl MultiWithChild for MultiPair +impl MultiWithChild for MultiPair

where C: FillVec, { @@ -112,13 +93,9 @@ where } } -impl WidgetBuilder for MultiPair { +impl WidgetBuilder for MultiPair

{ fn build(self, ctx: &BuildCtx) -> WidgetId { let MultiPair { parent, children } = self; - children - .into_iter() - .for_each(|child| ctx.force_as_mut().append_child(parent, child)); - - parent + parent.append_children(children, ctx.force_as_mut()) } } diff --git a/core/src/widget_children/single_child_impl.rs b/core/src/widget_children/single_child_impl.rs index 0e0761772..05d2d9306 100644 --- a/core/src/widget_children/single_child_impl.rs +++ b/core/src/widget_children/single_child_impl.rs @@ -10,8 +10,20 @@ pub trait SingleWithChild { /// A node of widget with not compose its child. pub struct SinglePair { - pub widget: W, - pub child: C, + pub(crate) widget: W, + pub(crate) child: C, +} + +impl SinglePair { + #[inline] + pub fn unzip(self) -> (W, C) { + let Self { widget, child } = self; + (widget, child) + } + #[inline] + pub fn child(self) -> C { self.child } + #[inline] + pub fn parent(self) -> W { self.widget } } impl SingleChild for Option {} @@ -35,77 +47,29 @@ impl SingleWithChild for SinglePair { } } -trait SingleParent { - fn into_single_parent(self, ctx: &mut BuildCtx) -> WidgetId; -} - -trait WidgetChild { - fn child_build(self, ctx: &BuildCtx) -> WidgetId; -} - -impl SingleParent for W { - #[inline] - fn into_single_parent(self, ctx: &mut BuildCtx) -> WidgetId { self.into_render_parent(ctx) } -} - -impl SingleParent for Pipe { - #[inline] - fn into_single_parent(self, ctx: &mut BuildCtx) -> WidgetId { - self.into_only_parent(W::into_render_parent, ctx) - } -} - -impl SingleParent for Pipe> { - #[inline] - fn into_single_parent(self, ctx: &mut BuildCtx) -> WidgetId { - self.into_only_parent( - |w, ctx| { - if let Some(w) = w { - w.into_render_parent(ctx) - } else { - Void.into_render_parent(ctx) - } - }, - ctx, - ) - } -} - -impl WidgetChild for Widget { - #[inline] - fn child_build(self, ctx: &BuildCtx) -> WidgetId { self.build(ctx) } -} - -impl WidgetChild for W { - #[inline] - fn child_build(self, ctx: &BuildCtx) -> WidgetId { self.build(ctx) } -} - impl WidgetBuilder for SinglePair where W: SingleParent, - C: WidgetChild, + C: Into, { fn build(self, ctx: &BuildCtx) -> WidgetId { let Self { widget, child } = self; - let p = widget.into_single_parent(ctx.force_as_mut()); - let child = child.child_build(ctx); - ctx.force_as_mut().append_child(p, child); - p + let child = child.into().build(ctx); + widget.append_child(child, ctx.force_as_mut()) } } impl WidgetBuilder for SinglePair, C> where W: SingleParent, - C: WidgetChild, + C: Into, { fn build(self, ctx: &BuildCtx) -> WidgetId { let Self { widget, child } = self; if let Some(widget) = widget { SinglePair { widget, child }.build(ctx) } else { - child.child_build(ctx) + child.into().build(ctx) } } } @@ -113,14 +77,14 @@ where impl WidgetBuilder for SinglePair> where W: SingleParent, - C: WidgetChild, + SinglePair: WidgetBuilder, { fn build(self, ctx: &BuildCtx) -> WidgetId { let Self { widget, child } = self; if let Some(child) = child { SinglePair { widget, child }.build(ctx) } else { - widget.into_single_parent(ctx.force_as_mut()) + widget.build(ctx) } } } diff --git a/tests/rdl_macro_test.rs b/tests/rdl_macro_test.rs index 242e772c3..5ad779b36 100644 --- a/tests/rdl_macro_test.rs +++ b/tests/rdl_macro_test.rs @@ -232,7 +232,7 @@ fn pipe_multi_parent() { let stack_or_flex2 = stack_or_flex.clone(); let w = fn_widget! { let container = pipe! { - let c: Box = if *$stack_or_flex { + let c: Box = if *$stack_or_flex { Box::new(rdl! { Stack { } }) } else { Box::new(rdl! { Flex { } })