Skip to content

Commit

Permalink
refactor(core): 💡 clean with_child
Browse files Browse the repository at this point in the history
  • Loading branch information
M-Adoo committed Sep 7, 2023
1 parent 54009ab commit 2bd5456
Show file tree
Hide file tree
Showing 9 changed files with 269 additions and 228 deletions.
98 changes: 40 additions & 58 deletions core/src/builtin_widgets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,69 +185,65 @@ impl<T> FatObj<T> {
}
}

impl<T: 'static> WidgetBuilder for FatObj<T>
where
T: Into<Widget>,
{
impl<T: Into<Widget>> WidgetBuilder for FatObj<T> {
fn build(self, ctx: &BuildCtx) -> WidgetId {
let Self { host, builtin } = self;
builtin.compose_with_host(host.into(), ctx).build(ctx)
}
}

impl<T: SingleWithChild<C>, C> SingleWithChild<C> for FatObj<T> {
type Target = FatObj<T::Target>;
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<T: SingleChild> SingleChild for FatObj<T> {}
impl<T: MultiChild> MultiChild for FatObj<T> {}
impl<T: ComposeChild + 'static> ComposeChild for FatObj<State<T>> {
type Child = T::Child;

fn compose_child(this: State<Self>, 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<T: MultiWithChild<C>, C> MultiWithChild<C> for FatObj<T> {
type Target = FatObj<T::Target>;
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<T: ComposeChild + 'static> ComposeChild for FatObj<T> {
type Child = T::Child;

fn compose_child(this: State<Self>, 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<T, M, C> ComposeWithChild<C, M> for FatObj<T>
where
T: ComposeWithChild<C, M>,
{
type Target = FatObj<T::Target>;
fn with_child(self, child: C, ctx: &BuildCtx) -> Self::Target {
impl<T: SingleParent + 'static> SingleParent for FatObj<T> {
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<T, C> SingleWithChild<C> for Option<FatObj<T>>
where
T: SingleWithChild<C>,
C: Into<Widget>,
<FatObj<T> as SingleWithChild<C>>::Target: Into<Widget>,
{
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<T: MultiParent + 'static> MultiParent for FatObj<T> {
fn append_children(self, children: Vec<WidgetId>, 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<C> ComposeWithChild<C, ()> for BuiltinObj
where
C: Into<Widget>,
{
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<Self>, child: Self::Child) -> Widget {
let this = this.into_inner_value();
fn_widget! { this.compose_with_host(child, ctx!()) }.into()
}
}

Expand All @@ -261,17 +257,3 @@ impl<T> std::ops::DerefMut for FatObj<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.host }
}

impl<W> BoxedSingleParent for FatObj<W>
where
W: SingleChild + Into<Box<dyn Render>> + Into<Widget> + 'static,
{
fn into_parent(self: Box<Self>, ctx: &mut BuildCtx) -> WidgetId { self.build(ctx) }
}

impl<W> BoxMultiParent for FatObj<W>
where
W: MultiChild + Into<Box<dyn Render>> + Into<Widget> + 'static,
{
fn into_parent(self: Box<Self>, ctx: &mut BuildCtx) -> WidgetId { self.build(ctx) }
}
125 changes: 88 additions & 37 deletions core/src/pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ use rxrust::{
use std::{
cell::{Cell, RefCell},
convert::Infallible,
ops::Range,
rc::Rc,
};

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,
};

Expand Down Expand Up @@ -115,47 +119,94 @@ impl<W: Into<Widget> + 'static> WidgetBuilder for Pipe<Option<W>> {
}
}

impl<R: 'static> Pipe<R> {
pub(crate) fn into_only_parent(
self,
mut f: impl FnMut(R, &mut BuildCtx) -> WidgetId + 'static,
ctx: &mut BuildCtx,
) -> WidgetId {
impl<W: SingleParent + 'static> SingleParent for Pipe<W> {
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<W: MultiParent + 'static> MultiParent for Pipe<W> {
fn append_children(self, children: Vec<WidgetId>, 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::<Vec<_>>();
new_p.append_children(children, ctx)
});
p
}
}
}

ctx.insert_after(id, new_id);
ctx.dispose_subtree(id);
impl<W: SingleParent + 'static> SingleParent for Pipe<Option<W>> {
fn append_child(self, child: WidgetId, ctx: &mut BuildCtx) -> WidgetId {
self
.map(|p| -> Box<dyn BoxedSingleParent> {
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<WidgetId>, tree: &WidgetTree) -> Range<WidgetId> {
rg.start..rg.end.parent(&tree.arena).unwrap()
}

attach_unsubscribe_guard(id, ctx.window().id(), unsub);
fn update_pipe_parent<W: 'static>(
// The range of the pipe parent widget ids.
parent: Range<WidgetId>,
// 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<W: 'static> Pipe<Multi<W>> {
Expand Down
31 changes: 18 additions & 13 deletions core/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};

Expand Down Expand Up @@ -162,18 +162,6 @@ impl<T> Share for State<T> {
impl<W: SingleChild> SingleChild for State<W> {}
impl<W: MultiChild> MultiChild for State<W> {}

impl<W: SingleChild + Render + 'static> BoxedSingleParent for State<W> {
fn into_parent(self: Box<Self>, ctx: &mut BuildCtx) -> WidgetId {
ctx.alloc_widget((*self).into())
}
}

impl<W: MultiChild + Render + 'static> BoxMultiParent for State<W> {
fn into_parent(self: Box<Self>, ctx: &mut BuildCtx) -> WidgetId {
ctx.alloc_widget((*self).into())
}
}

impl<W: Render + 'static> From<State<W>> for Box<dyn Render> {
#[inline]
fn from(s: State<W>) -> Self {
Expand Down Expand Up @@ -205,6 +193,23 @@ impl<W> State<W> {
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<W> {
match self.0.into_inner() {
InnerState::Data(w) => {
Expand Down
5 changes: 3 additions & 2 deletions core/src/state/stateful.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,9 @@ impl<W> Stateful<W> {
}

pub(crate) fn try_into_inner(self) -> Result<W, Self> {
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)
Expand Down
Loading

0 comments on commit 2bd5456

Please sign in to comment.