From 1d72d7cb7d14ec67973e68fc12b78fce31c42200 Mon Sep 17 00:00:00 2001 From: Adoo Date: Fri, 13 Dec 2024 21:40:11 +0800 Subject: [PATCH] =?UTF-8?q?refactor(core):=20=F0=9F=92=A1=20clarifying=20t?= =?UTF-8?q?he=20logic=20of=20how=20a=20parent=20selects=20its=20children?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/src/builtin_widgets.rs | 90 +++-- core/src/builtin_widgets/theme/icon_theme.rs | 2 +- core/src/declare.rs | 6 +- core/src/pipe.rs | 1 + core/src/state.rs | 14 - core/src/widget.rs | 3 +- core/src/widget_children.rs | 326 ++++++++++++++---- core/src/widget_children/child_convert.rs | 113 ------ .../src/widget_children/compose_child_impl.rs | 312 +++++++++-------- .../src/widget_children/into_child_compose.rs | 48 +++ core/src/widget_children/multi_child_impl.rs | 213 ++++++------ core/src/widget_children/single_child_impl.rs | 197 +++++------ examples/messages/src/messages.rs | 4 +- examples/storybook/src/storybook.rs | 18 +- examples/todos/src/ui.rs | 4 +- macros/src/child_template.rs | 122 +++---- macros/src/declare_derive.rs | 66 ++-- macros/src/lib.rs | 34 +- tests/child_template_derive_test.rs | 5 + tests/rdl_macro_test.rs | 18 +- widgets/src/checkbox.rs | 26 +- widgets/src/common_widget.rs | 72 ++-- widgets/src/input.rs | 3 +- widgets/src/label.rs | 3 +- widgets/src/layout/expanded.rs | 2 +- widgets/src/lists.rs | 20 +- widgets/src/tabs.rs | 1 + widgets/src/text_field.rs | 8 +- 28 files changed, 965 insertions(+), 766 deletions(-) delete mode 100644 core/src/widget_children/child_convert.rs create mode 100644 core/src/widget_children/into_child_compose.rs diff --git a/core/src/builtin_widgets.rs b/core/src/builtin_widgets.rs index 417c952c8..1d8c1cc01 100644 --- a/core/src/builtin_widgets.rs +++ b/core/src/builtin_widgets.rs @@ -724,7 +724,7 @@ impl FatObj { /// tab_index value, their order relative to each other follows their /// position in the tree source. The maximum value for tab_index is 32767. /// If not specified, it takes the default value 0. - pub fn tab_index(self, tab_idx: impl DeclareInto) -> Self { + pub fn tab_index(self, tab_idx: impl DeclareInto) -> Self { self.declare_builtin_init( tab_idx, |this| this.get_mix_builtin_widget().mix_flags(), @@ -733,7 +733,7 @@ impl FatObj { } /// Initializes the `Class` that should be applied to the widget. - pub fn class(self, cls: impl DeclareInto) -> Self { + pub fn class(self, cls: impl DeclareInto) -> Self { self.declare_builtin_init(cls, Self::get_class_widget, |c, cls| c.class = Some(cls)) } @@ -742,7 +742,7 @@ impl FatObj { /// /// Only one widget should have this attribute specified. If there are /// several, the widget nearest the root, get the initial focus. - pub fn auto_focus(self, v: impl DeclareInto) -> Self { + pub fn auto_focus(self, v: impl DeclareInto) -> Self { self.declare_builtin_init( v, |this| this.get_mix_builtin_widget().mix_flags(), @@ -751,137 +751,137 @@ impl FatObj { } /// Initializes how its child should be scale to fit its box. - pub fn box_fit(self, v: impl DeclareInto) -> Self { + pub fn box_fit(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_fitted_box_widget, |m, v| m.box_fit = v) } /// Initializes the painting style of this widget. - pub fn painting_style(self, v: impl DeclareInto) -> Self { + pub fn painting_style(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_painting_style_widget, |m, v| m.painting_style = v) } /// Initializes the text style of this widget. - pub fn text_style(self, v: impl DeclareInto) -> Self { + pub fn text_style(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_text_style_widget, |m, v| m.text_style = v) } /// Initializes the font size of this widget. - pub fn font_size(self, v: impl DeclareInto) -> Self { + pub fn font_size(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_text_style_widget, |m, v| m.text_style.font_size = v) } /// Initializes the font face of this widget. - pub fn font_face(self, v: impl DeclareInto) -> Self { + pub fn font_face(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_text_style_widget, |m, v| m.text_style.font_face = v) } /// Initializes the letter space of this widget. - pub fn letter_spacing(self, v: impl DeclareInto) -> Self { + pub fn letter_spacing(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_text_style_widget, |m, v| m.text_style.letter_space = v) } /// Initializes the text line height of this widget. - pub fn text_line_height(self, v: impl DeclareInto) -> Self { + pub fn text_line_height(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_text_style_widget, |m, v| m.text_style.line_height = v) } /// Initializes the text overflow of this widget. - pub fn text_overflow(self, v: impl DeclareInto) -> Self { + pub fn text_overflow(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_text_style_widget, |m, v| m.text_style.overflow = v) } /// Initializes the background of the widget. - pub fn background(self, v: impl DeclareInto, M>) -> Self { + pub fn background(self, v: impl DeclareInto, M>) -> Self { self.declare_builtin_init(v, Self::get_box_decoration_widget, |m, v| m.background = v) } /// Initializes the foreground of the widget. - pub fn foreground(self, v: impl DeclareInto) -> Self { + pub fn foreground(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_foreground_widget, |m, v| m.foreground = v) } /// Initializes the border of the widget. - pub fn border(self, v: impl DeclareInto, M>) -> Self { + pub fn border(self, v: impl DeclareInto, M>) -> Self { self.declare_builtin_init(v, Self::get_box_decoration_widget, |m, v| m.border = v) } /// Initializes the border radius of the widget. - pub fn border_radius(self, v: impl DeclareInto, M>) -> Self { + pub fn border_radius(self, v: impl DeclareInto, M>) -> Self { self.declare_builtin_init(v, Self::get_box_decoration_widget, |m, v| m.border_radius = v) } /// Initializes the extra space within the widget. - pub fn padding(self, v: impl DeclareInto) -> Self { + pub fn padding(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_padding_widget, |m, v| m.padding = v) } /// Initializes the cursor of the widget. - pub fn cursor(self, v: impl DeclareInto) -> Self { + pub fn cursor(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_cursor_widget, |m, v| m.cursor = v) } /// Initializes the space around the widget. - pub fn margin(self, v: impl DeclareInto) -> Self { + pub fn margin(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_margin_widget, |m, v| m.margin = v) } /// Initializes the constraints clamp of the widget. - pub fn clamp(self, v: impl DeclareInto) -> Self { + pub fn clamp(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_constrained_box_widget, |m, v| m.clamp = v) } /// Initializes how user can scroll the widget. - pub fn scrollable(self, v: impl DeclareInto) -> Self { + pub fn scrollable(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_scrollable_widget, |m, v| m.scrollable = v) } /// Initializes the transformation of the widget. - pub fn transform(self, v: impl DeclareInto) -> Self { + pub fn transform(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_transform_widget, |m, v| m.transform = v) } /// Initializes how the widget should be aligned horizontally. - pub fn h_align(self, v: impl DeclareInto) -> Self { + pub fn h_align(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_h_align_widget, |m, v| m.h_align = v) } /// Initializes how the widget should be aligned vertically. - pub fn v_align(self, v: impl DeclareInto) -> Self { + pub fn v_align(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_v_align_widget, |m, v| m.v_align = v) } /// Initializes the relative anchor to the parent of the widget. - pub fn anchor(self, v: impl DeclareInto) -> Self { + pub fn anchor(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_relative_anchor_widget, |m, v| m.anchor = v) } /// Initializes the horizontal global anchor of the widget. - pub fn global_anchor_x(self, v: impl DeclareInto) -> Self { + pub fn global_anchor_x(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_global_anchor_widget, |m, v| m.global_anchor_x = v) } /// Initializes the vertical global anchor of the widget. - pub fn global_anchor_y(self, v: impl DeclareInto) -> Self { + pub fn global_anchor_y(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_global_anchor_widget, |m, v| m.global_anchor_y = v) } /// Initializes the visibility of the widget. - pub fn visible(self, v: impl DeclareInto) -> Self { + pub fn visible(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_visibility_widget, |m, v| m.visible = v) } /// Initializes the opacity of the widget. - pub fn opacity(self, v: impl DeclareInto) -> Self { + pub fn opacity(self, v: impl DeclareInto) -> Self { self.declare_builtin_init(v, Self::get_opacity_widget, |m, v| m.opacity = v) } /// Initializes the tooltips of the widget. - pub fn tooltips(self, v: impl DeclareInto, M>) -> Self { + pub fn tooltips(self, v: impl DeclareInto, M>) -> Self { self.declare_builtin_init(v, Self::get_tooltips_widget, |m, v| m.tooltips = v) } /// Initializes the `keep_alive` value of the `KeepAlive` widget. - pub fn keep_alive(mut self, v: impl DeclareInto) -> Self { + pub fn keep_alive(mut self, v: impl DeclareInto) -> Self { let (v, o) = v.declare_into().unzip(); let d = self.get_keep_alive_widget(); d.write().keep_alive = v; @@ -908,7 +908,7 @@ impl FatObj { self } - fn declare_builtin_init( + fn declare_builtin_init( mut self, init: impl DeclareInto, get_builtin: impl FnOnce(&mut Self) -> &State, set_value: fn(&mut B, V), ) -> Self { @@ -1059,12 +1059,30 @@ where } } -impl<'w, T, C, const N: usize, const M: usize> WithChild<'w, C, N, M> - for DeclarerWithSubscription +impl SingleChild for DeclarerWithSubscription { + fn with_child<'c, const M: usize>(self, child: impl IntoChildSingle<'c, M>) -> Widget<'c> { + self.map(|w| w.with_child(child)).into_widget() + } + + fn into_parent(self: Box) -> Widget<'static> { (*self).into_widget() } +} + +impl MultiChild for DeclarerWithSubscription { + fn with_child<'c, const N: usize, const M: usize>( + self, child: impl IntoChildMulti<'c, N, M>, + ) -> MultiPair<'c> { + MultiPair::new(self, child) + } + + fn into_parent(self: Box) -> Widget<'static> { (*self).into_widget() } +} + +impl<'w, T, C, const TML: bool, const WRITER: bool, const N: usize, const M: usize> + ComposeWithChild<'w, C, WRITER, TML, N, M> for DeclarerWithSubscription where - T: WithChild<'w, C, N, M> + 'w, - C: 'w, + T: ComposeWithChild<'w, C, WRITER, TML, N, M>, { type Target = DeclarerWithSubscription; - fn with_child(self, c: C) -> Self::Target { self.map(|w| w.with_child(c)) } + + fn with_child(self, child: C) -> Self::Target { self.map(|host| host.with_child(child)) } } diff --git a/core/src/builtin_widgets/theme/icon_theme.rs b/core/src/builtin_widgets/theme/icon_theme.rs index 1326b5988..bfd4803cb 100644 --- a/core/src/builtin_widgets/theme/icon_theme.rs +++ b/core/src/builtin_widgets/theme/icon_theme.rs @@ -56,7 +56,7 @@ macro_rules! fill_svgs { pub const CUSTOM_ICON_START: NamedSvg = NamedSvg::new(65536); /// The identify of a svg define in theme. -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, ChildOfCompose)] pub struct NamedSvg(pub usize); impl Compose for NamedSvg { diff --git a/core/src/declare.rs b/core/src/declare.rs index c4ef55764..cba0c2008 100644 --- a/core/src/declare.rs +++ b/core/src/declare.rs @@ -20,13 +20,13 @@ pub trait ObjDeclarer { } /// Used to do conversion from a value to the `DeclareInit` type. -pub trait DeclareFrom { +pub trait DeclareFrom { fn declare_from(value: V) -> Self; } /// A value-to-value conversion that consumes the input value. The /// opposite of [`DeclareFrom`]. -pub trait DeclareInto { +pub trait DeclareInto { fn declare_into(self) -> DeclareInit; } @@ -77,7 +77,7 @@ where } } -impl DeclareInto for T +impl DeclareInto for T where DeclareInit: DeclareFrom, { diff --git a/core/src/pipe.rs b/core/src/pipe.rs index 02a32cf6e..3f1d8972f 100644 --- a/core/src/pipe.rs +++ b/core/src/pipe.rs @@ -65,6 +65,7 @@ pub trait Pipe: 'static { /// has a better conversion from `Pipe` to `BoxPipe`. /// /// Call `into_pipe` to convert it to a `Pipe` type. +#[derive(ChildOfCompose)] pub struct BoxPipe(Box>); pub struct MapPipe { diff --git a/core/src/state.rs b/core/src/state.rs index 3e5cc048e..ea12bf331 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -424,20 +424,6 @@ impl IntoWidgetStrict<'static, COMPOSE> for State { fn into_widget_strict(self) -> Widget<'static> { Compose::compose(self) } } -impl MultiChild for T -where - T: StateReader, - T::Value: MultiChild, -{ -} - -impl SingleChild for T -where - T: StateReader, - T::Value: SingleChild, -{ -} - #[cfg(test)] mod tests { use std::cell::Cell; diff --git a/core/src/widget.rs b/core/src/widget.rs index 97a4de4e7..517ee48c5 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -88,7 +88,7 @@ impl<'w> LazyNode<'w> { /// A boxed function widget that can be called multiple times to regenerate /// widget. -#[derive(Clone)] +#[derive(Clone, ChildOfCompose)] pub struct GenWidget(InnerGenWidget); type InnerGenWidget = Sc Widget<'static>>>>; @@ -98,6 +98,7 @@ type InnerGenWidget = Sc Widget<'static>>>>; /// It already implements `IntoChild`, allowing any function widget to be /// converted to `FnWidget`. Therefore, using `FnWidget` as the child type of /// `ComposeChild` enables the acceptance of all function widgets. +#[derive(ChildOfCompose)] pub struct FnWidget<'w>(Box Widget<'w> + 'w>); // The widget type marker. diff --git a/core/src/widget_children.rs b/core/src/widget_children.rs index 6b6a3c54c..54994c4f7 100644 --- a/core/src/widget_children.rs +++ b/core/src/widget_children.rs @@ -5,44 +5,248 @@ mod single_child_impl; pub use compose_child_impl::*; pub use multi_child_impl::*; pub use single_child_impl::*; -pub mod child_convert; -pub use child_convert::IntoChild; - -/// Trait to mark a widget can have one widget as child. -pub trait SingleChild {} -/// Trait to tell Ribir a object that has multi children. -pub trait MultiChild {} -/// A boxed render widget that support accept one child. -#[derive(SingleChild)] -pub struct BoxedSingleChild(Widget<'static>); - -/// A boxed render widget that support accept multi children. -#[derive(MultiChild)] -pub struct BoxedMultiChild(Widget<'static>); - -/// This trait specifies the type of child a widget can have, and the target -/// type represents the result of the widget composing its child. -/// -/// The N and M markers are used to avoid implementation conflicts. If Rust -/// supports generic specialization, we could avoid using them. -/// -/// The M marker is used for child conversion. -/// The N marker is used to distinguish the parent type: -/// - 0 for SingleChild -/// - 1 for MultiChild -/// - 2..9 for ComposeChild -pub trait WithChild<'w, C, const N: usize, const M: usize> { - type Target: 'w; - fn with_child(self, child: C) -> Self::Target; +pub mod into_child_compose; + +/// The trait is for a widget that can have only one child. +/// +/// Use `#[derive(SingleChild)]` for implementing this trait. It's best to use +/// the derive method first; manual implementation is not suggested unless you +/// fully understand how widget composition works in the framework. +pub trait SingleChild: IntoWidget<'static, RENDER> { + /// Compose the child to a new widget. + fn with_child<'c, const M: usize>(self, child: impl IntoChildSingle<'c, M>) -> Widget<'c> + where + Self: Sized; + + fn into_parent(self: Box) -> Widget<'static>; +} + +/// The trait is for a widget that can have more than one children. +/// +/// Use `#[derive(MultiChild)]` for implementing this trait. It's best to use +/// the derive method first; manual implementation is not suggested unless you +/// fully understand how widget composition works in the framework. +pub trait MultiChild: IntoWidget<'static, RENDER> { + fn with_child<'c, const N: usize, const M: usize>( + self, child: impl IntoChildMulti<'c, N, M>, + ) -> MultiPair<'c> + where + Self: Sized; + + fn into_parent(self: Box) -> Widget<'static>; } -/// Trait for specifying the child type and implementing how to compose the -/// child. +/// Trait for specifying the child type and defining how to compose the child. +/// +/// ## Child Conversion +/// +/// `ComposeChild` only accepts children that can be converted to +/// `ComposeChild::Child` by implementing `IntoChildCompose`. If the child is a +/// [`Template`], it allows for more flexibility. +/// +/// ### Basic Conversion +/// +/// The most basic child type is `Widget<'c>`, which automatically converts any +/// widget to it. This allows you to compose any widget. +/// +/// +/// ```rust +/// use ribir::prelude::*; +/// +/// #[derive(Declare)] +/// struct X; +/// +/// impl<'c> ComposeChild<'c> for X { +/// type Child = Widget<'c>; +/// +/// fn compose_child(this: impl StateWriter, child: Self::Child) -> Widget<'c> { +/// let w = FatObj::new(child); +/// w.background(Color::RED).into_widget() +/// } +/// } +/// +/// // You can compose `X` with any widget, and `X` will automatically apply a background color to it. +/// +/// let _with_container = x! { +/// @Container { size: Size::splat(100.) } +/// }; +/// +/// let _with_text = x! { +/// @Text { text: "Hi!" } +/// }; +/// ``` +/// +/// If you want to compose a custom type, you can derive [`ChildOfCompose`] for +/// it to restrict composition to only that type. Additionally, you can +/// implement [`ComposeChildFrom`] to enable the composition of more types. +/// ```rust +/// use ribir::prelude::*; +/// +/// #[derive(Declare)] +/// struct X; +/// +/// #[derive(ChildOfCompose)] +/// struct A; +/// +/// impl ComposeChild<'static> for X { +/// type Child = A; +/// +/// fn compose_child(this: impl StateWriter, child: Self::Child) -> Widget<'static> { +/// unimplemented!() +/// } +/// } +/// +/// // Only A is supported as a child of X. +/// let _only_a = x! { +/// @ { A } +/// }; +/// +/// struct B; +/// +/// impl ComposeChildFrom for A { +/// fn compose_child_from(_: B) -> Self { A } +/// } +/// +/// // After implementing `ComposeChildFrom` for `A`, now `B` can also be a child of `X`. +/// let _with_a = x! { @ { A } }; +/// let _with_b = x! { @ { B } }; +/// ``` +/// +/// ### Template Child +/// +/// Templates outline the shape of children for `ComposeChild` and offer more +/// flexible child conversion. +/// ```rust +/// use ribir::prelude::*; +/// +/// #[derive(Declare)] +/// struct X; +/// +/// #[derive(ChildOfCompose)] +/// struct B; +/// +/// #[derive(Template)] +/// struct XChild { +/// a: Widget<'static>, +/// b: Option, +/// } +/// +/// impl<'c> ComposeChild<'c> for X { +/// type Child = XChild; +/// +/// fn compose_child(_: impl StateWriter, _: Self::Child) -> Widget<'c> { +/// unimplemented!() +/// } +/// } +/// +/// // The template child allows `X` to have two children: a widget and a `B`, where `B` is optional. +/// +/// let _with_only_widget = x! { @Container { size: Size::splat(100.) } }; +/// let _with_widget_and_b = x! { +/// @Container { size: Size::splat(100.) } +/// @ { B } +/// }; +/// ``` +/// +/// Templates can also be enums, see [`Template`] for more details. pub trait ComposeChild<'c>: Sized { type Child: 'c; fn compose_child(this: impl StateWriter, child: Self::Child) -> Widget<'c>; } +/// The trait converts a type into a child of the `SingleChild`. +pub trait IntoChildSingle<'c, const M: usize> { + fn into_child_single(self) -> Option>; +} + +/// The trait converts a type into a child of the `MultiChild`. +pub trait IntoChildMulti<'c, const N: usize, const M: usize> { + fn into_child_multi(self) -> impl Iterator>; +} + +/// Trait for conversions type as a child of widget. The opposite of +/// `ComposeChildFrom`. +/// +/// You should not directly implement this trait. Instead, implement +/// `ComposeChildFrom`. +/// +/// It is similar to `Into` but with a const marker to automatically implement +/// all possible conversions without implementing conflicts. +pub trait IntoChildCompose { + fn into_child_compose(self) -> C; +} + +/// Used to do value-to-value conversions while consuming the input value. It is +/// the reciprocal of `IntoChildCompose`. +/// +/// One should always prefer implementing `ComposeChildFrom` over +/// `IntoChildCompose`, because implementing `ComposeChildFrom` will +/// automatically implement `IntoChildCompose`. +pub trait ComposeChildFrom { + fn compose_child_from(from: C) -> Self; +} + +/// This trait signifies that a type can serve as a child of `ComposeChild`. +/// +/// Implementing this trait involves implementing `ComposeChildFrom` from this +/// type to itself. +/// +/// One can utilize `#[derive(ChildOfCompose)]` to implement this trait. +pub trait ChildOfCompose {} + +/// The template specifies the types of children that `ComposeChild` can have, +/// gathering these children and providing them to the parent for composition. +/// +/// You can use `#[derive(Template)]` to implement this trait for a struct or +/// enum. +/// +/// In a struct, children are collected from its fields, so the field types must +/// be distinct and not convertible between each other using `ComposeChildFrom`. +/// +/// In an enum, children are collected from its variants, so the variant types +/// must also be distinct and not convertible between each other using +/// `ComposeChildFrom`. +/// +/// # Example +/// +/// ```rust +/// use ribir::prelude::*; +/// +/// #[derive(Template)] +/// struct MyTemplate<'w> { +/// leading_icon: Leading>, +/// trailing_icon: Option>>, +/// } +/// ``` +/// +/// This template outlines two child components for its parent: a mandatory +/// `Leading` and an optional `Trailing`. +/// +/// ```rust +/// use ribir::prelude::*; +/// +/// #[derive(Template)] +/// enum MyTemplate<'w> { +/// Leading(Leading>), +/// Trailing(Trailing>), +/// } +/// ``` +/// +/// This template specifies one child for its parent, which must be either a +/// leading icon or a trailing icon. +/// +/// Refer to the [`ComposeChild`] documentation for further information. +pub trait Template: Sized { + type Builder: TemplateBuilder; + fn builder() -> Self::Builder; +} + +/// The builder of a template. +pub trait TemplateBuilder: Sized { + type Target; + fn build_tml(self) -> Self::Target; +} + /// A pair of object and its child without compose, this keep the type /// information of parent and child. `PairChild` and `ComposeChild` can create a /// `Pair` with its child. @@ -55,24 +259,14 @@ pub struct Pair { /// type. pub type WidgetOf<'a, W> = Pair>; -impl IntoWidgetStrict<'static, RENDER> for BoxedMultiChild { - #[inline] - fn into_widget_strict(self) -> Widget<'static> { self.0 } -} - -impl IntoWidgetStrict<'static, RENDER> for BoxedSingleChild { - #[inline] - fn into_widget_strict(self) -> Widget<'static> { self.0 } -} - -impl BoxedSingleChild { +impl IntoWidgetStrict<'static, RENDER> for Box { #[inline] - pub fn new(widget: impl SingleIntoParent) -> Self { Self(widget.into_parent()) } + fn into_widget_strict(self) -> Widget<'static> { self.into_parent() } } -impl BoxedMultiChild { +impl IntoWidgetStrict<'static, RENDER> for Box { #[inline] - pub fn new(widget: impl MultiIntoParent) -> Self { Self(widget.into_parent()) } + fn into_widget_strict(self) -> Widget<'static> { self.into_parent() } } impl Pair { @@ -101,22 +295,26 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] #[test] + #[allow(dead_code)] fn compose_template_child() { reset_test_env!(); #[derive(Declare)] struct Page; - #[derive(Declare, SingleChild)] - struct Header; - #[derive(Declare, SingleChild)] - struct Content; - #[derive(Declare, SingleChild)] - struct Footer; + + #[derive(Template)] + struct Header<'w>(Widget<'w>); + + #[derive(Template)] + struct Content<'w>(Widget<'w>); + + #[derive(Template)] + struct Footer<'w>(Widget<'w>); #[derive(Template)] struct PageTml<'w> { - _header: WidgetOf<'w, FatObj
>, - _content: WidgetOf<'w, FatObj>, - _footer: WidgetOf<'w, FatObj