use crate::prelude::*;
mod compose_child_impl;
mod multi_child_impl;
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, IntoEnumVariable};
pub trait SingleChild {}
pub trait MultiChild {}
pub trait ComposeChild: Sized {
type Child;
fn compose_child(this: State<Self>, child: Self::Child) -> Widget;
}
pub type WidgetOf<W> = WidgetPair<W, Widget>;
#[cfg(test)]
mod tests {
use super::*;
use crate::test_helper::*;
use ribir_dev_helper::*;
use std::{cell::RefCell, rc::Rc};
#[test]
fn compose_template_child() {
#[derive(Declare)]
struct Page;
#[derive(Declare, SingleChild)]
struct Header;
#[derive(Declare, SingleChild)]
struct Content;
#[derive(Declare, SingleChild)]
struct Footer;
#[derive(Template)]
struct PageTml {
_header: WidgetOf<Header>,
_content: WidgetOf<Content>,
_footer: WidgetOf<Footer>,
}
impl ComposeChild for Page {
type Child = PageTml;
fn compose_child(_: State<Self>, _: Self::Child) -> Widget {
unreachable!("Only for syntax support check");
}
}
widget! {
Page {
Header { Void {} }
Content { Void {} }
Footer { Void {} }
}
};
}
#[test]
fn compose_option_child() {
#[derive(Declare)]
struct Parent;
#[derive(Declare, SingleChild)]
struct Child;
impl ComposeChild for Parent {
type Child = Option<WidgetPair<Child, Widget>>;
fn compose_child(_: State<Self>, _: Self::Child) -> Widget {
unreachable!("Only for syntax support check");
}
}
widget! {
Parent {
Child { Void {} }
}
};
}
#[test]
fn compose_option_dyn_parent() {
widget! {
DynWidget {
dyns: widget::then(true, || MockBox { size: Size::zero() }),
Void {}
}
};
}
#[test]
fn tuple_as_vec() {
#[derive(Declare)]
struct A;
#[derive(Declare)]
struct B;
impl ComposeChild for A {
type Child = Vec<B>;
fn compose_child(_: State<Self>, _: Self::Child) -> Widget {
unreachable!("Only for syntax support check");
}
}
widget! {
A {
B {}
B {}
}
};
}
#[test]
fn expr_with_child() {
let size = Stateful::new(Size::zero());
let _e = widget! {
states { size: size.clone() }
DynWidget {
dyns: if size.area() > 0. {
MockBox { size: *size }
} else {
MockBox { size: Size::new(1., 1.) }
},
MockBox { size: *size }
}
};
let _e = widget! {
DynWidget {
dyns: MockMulti {},
MockBox { size: Size::zero() }
MockBox { size: Size::zero() }
MockBox { size: Size::zero() }
}
};
let _e = widget! {
states { size: size.clone() }
DynWidget {
dyns: widget::then(size.area() > 0., || MockBox { size: Size::zero() }),
MockBox { size: Size::zero() }
}
};
let _e = widget! {
states { size: size }
DynWidget {
dyns: widget::then(size.area() > 0., || MockBox { size: Size::zero() }),
DynWidget { dyns: Void.into_widget() }
}
};
}
#[test]
fn compose_const_dyn_option_widget() {
let _ = widget! {
MockBox {
size: ZERO_SIZE,
widget::then(true, || MockBox { size: Size::zero() })
}
};
}
#[test]
fn pair_to_pair() {
#[derive(Declare)]
struct P;
impl ComposeChild for P {
type Child = WidgetOf<MockBox>;
fn compose_child(_: State<Self>, _: Self::Child) -> Widget { unreachable!() }
}
let _ = widget! {
P { MockBox { Void {} } }
};
}
#[test]
fn fix_multi_fill_for_pair() {
struct X;
impl ComposeChild for X {
type Child = WidgetOf<MockBox>;
fn compose_child(_: State<Self>, _: Self::Child) -> Widget { Void.into_widget() }
}
let child = MockBox { size: ZERO_SIZE }.with_child(Void.into_widget());
X.with_child(child);
}
fn dyns_compose_child() -> Widget {
#[derive(Declare)]
struct X;
impl ComposeChild for X {
type Child = MockBox;
fn compose_child(_: State<Self>, child: Self::Child) -> Widget { child.into_widget() }
}
let dyns = Stateful::new(DynWidget { dyns: Some(X) });
let size = Size::new(100., 200.);
ComposeChild::compose_child(State::<X>::from(dyns), MockBox { size })
}
widget_layout_test!(dyns_compose_child, width == 100., height == 200.,);
const COMPOSE_DYNS_CHILD_SIZE: Size = Size::new(100., 200.);
fn compose_dyns_child() -> Widget {
#[derive(Declare)]
struct X;
impl ComposeChild for X {
type Child = State<MockBox>;
fn compose_child(_: State<Self>, child: Self::Child) -> Widget { child.into_widget() }
}
let trigger = Stateful::new(true);
widget! {
states { trigger: trigger }
X {
DynWidget {
dyns: if *trigger {
MockBox { size: COMPOSE_DYNS_CHILD_SIZE }
} else {
MockBox { size: ZERO_SIZE }
}
}
}
}
}
widget_layout_test!(compose_dyns_child, size == COMPOSE_DYNS_CHILD_SIZE,);
const FIX_OPTION_TEMPLATE_EXPECT_SIZE: Size = Size::new(100., 200.);
fn fix_option_template() -> Widget {
struct Field(String);
#[derive(Template, Default)]
pub struct ConfigTml {
_field: Option<Field>,
}
#[derive(Declare)]
struct Host {}
impl ComposeChild for Host {
type Child = Option<ConfigTml>;
fn compose_child(_: State<Self>, _: Self::Child) -> Widget {
widget! { MockBox { size: FIX_OPTION_TEMPLATE_EXPECT_SIZE } }
}
}
widget! {
Host { Field("test".into()) }
}
}
widget_layout_test!(
fix_option_template,
{ path = [0], size == FIX_OPTION_TEMPLATE_EXPECT_SIZE, }
);
#[test]
fn compose_dyn_multi_child() {
struct A;
impl ComposeChild for A {
type Child = Vec<Widget>;
fn compose_child(_: State<Self>, child: Self::Child) -> Widget {
MockMulti.with_child(child).into_widget()
}
}
let child = DynWidget { dyns: Some([Void]) };
let child = Stateful::new(child);
let cnt = Rc::new(RefCell::new(0));
let c_cnt = cnt.clone();
child
.modifies()
.subscribe(move |_| *c_cnt.borrow_mut() += 1);
let _ = TestWindow::new(A.with_child(child));
assert_eq!(*cnt.borrow(), 0);
}
#[test]
fn compose_multi_with_stateful_option() {
struct M;
impl ComposeChild for M {
type Child = Vec<Widget>;
fn compose_child(_: State<Self>, _: Self::Child) -> Widget { Void.into_widget() }
}
let c = Stateful::new(DynWidget { dyns: Some(Some(Void)) });
let _ = M.with_child(c).into_widget();
}
}