Skip to content

Commit

Permalink
refactor(core): 💡 lazy build widget
Browse files Browse the repository at this point in the history
  • Loading branch information
M-Adoo committed Aug 1, 2024
1 parent bc04965 commit 42f38c2
Show file tree
Hide file tree
Showing 102 changed files with 2,056 additions and 2,047 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,17 @@ fn main() {
let inc_btn = FilledButton::declarer()
.on_tap(move |_| *c_cnt.write() += 1)
.finish(ctx)
.with_child(Label::new("Inc"), ctx);
.with_child(Label::new("Inc"));

let counter = H1::declarer()
.text(pipe!($cnt.to_string()))
.finish(ctx);

Row::declarer()
.finish(ctx)
.with_child(inc_btn, ctx)
.with_child(counter, ctx)
.build(ctx)
.with_child(inc_btn)
.with_child(counter)
.into_widget()
};

App::run(counter);
Expand Down
3 changes: 2 additions & 1 deletion core/src/animation/stagger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ mod tests {
use super::*;
use crate::{reset_test_env, test_helper::*};

fn stagger_run_and_stop() -> impl IntoWidgetStrict<FN> {
fn stagger_run_and_stop() -> Widget<'static> {
fn_widget! {
let stagger = Stagger::new(Duration::from_millis(100), transitions::EASE_IN.of(ctx!()));
let mut mock_box = @MockBox { size: Size::new(100., 100.) };
Expand Down Expand Up @@ -253,6 +253,7 @@ mod tests {

mock_box
}
.into_widget()
}
widget_layout_test!(stagger_run_and_stop, width == 100., height == 100.,);

Expand Down
104 changes: 59 additions & 45 deletions core/src/builtin_widgets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ pub struct LazyWidgetId(Sc<Cell<Option<WidgetId>>>);
/// let w = multi.get_margin_widget().clone_writer();
/// multi
/// .on_tap(move |_| w.write().margin = EdgeInsets::all(20.))
/// .into_widget(ctx)
/// .into_widget()
/// };
/// ```
pub struct FatObj<T> {
Expand Down Expand Up @@ -125,11 +125,34 @@ pub struct FatObj<T> {
}

impl LazyWidgetId {
pub fn id(&self) -> Option<WidgetId> { self.0.get() }
/// Creates a new `LazyWidgetId` associated with a widget. You can retrieve
/// the widget's ID after the build process using this `LazyWidgetId`.
pub fn new(widget: Widget) -> (Widget, Self) {
let lazy_id = Self(<_>::default());
let w = lazy_id.clone().bind(widget);
(w, lazy_id)
}

/// Bind a widget to the LazyWidgetId, and return a widget that will set the
/// id to the LazyWidgetId after build.
pub fn bind(self, widget: Widget) -> Widget {
let f = move |ctx: &BuildCtx| {
let id = widget.build(ctx);
assert!(self.id().is_none(), "The LazyWidgetID only allows binding to one widget.");
self.0.set(Some(id));
id
};

pub fn assert_id(&self) -> WidgetId { self.0.get().unwrap() }
InnerWidget::LazyBuild(Box::new(f)).into()
}

fn set(&self, wid: WidgetId) { self.0.set(Some(wid)); }
pub fn id(&self) -> Option<WidgetId> { self.0.get() }

pub fn assert_id(&self) -> WidgetId {
self.0.get().expect(
"The binding is not associated with a widget, or the bound widget has not been built yet.",
)
}

fn ref_count(&self) -> usize { self.0.ref_count() }
}
Expand Down Expand Up @@ -845,97 +868,88 @@ impl<T> ObjDeclarer for FatObj<T> {
fn finish(self, _: &BuildCtx) -> Self::Target { self }
}

impl<T, const M: usize> IntoWidgetStrict<M> for FatObj<T>
impl<'w, T, const M: usize> IntoWidgetStrict<'w, M> for FatObj<T>
where
T: IntoWidget<M>,
T: IntoWidget<'w, M>,
{
#[inline]
fn into_widget_strict(self, ctx: &BuildCtx) -> Widget {
let mut host = self.host.into_widget(ctx);
self.host_id.set(host.id());
fn into_widget_strict(self) -> Widget<'w> { self.map(|w| w.into_widget()).compose() }
}

impl<'a> FatObj<Widget<'a>> {
fn compose(self) -> Widget<'a> {
let mut host = self.host;
host = self.host_id.clone().bind(host);
if let Some(mix_builtin) = self.mix_builtin {
host = mix_builtin.with_child(host, ctx).into_widget(ctx)
host = mix_builtin.with_child(host).into_widget()
}
if let Some(request_focus) = self.request_focus {
host = request_focus
.with_child(host, ctx)
.into_widget(ctx);
host = request_focus.with_child(host).into_widget();
}
if let Some(has_focus) = self.has_focus {
host = has_focus.with_child(host, ctx).into_widget(ctx);
host = has_focus.with_child(host).into_widget();
}
if let Some(mouse_hover) = self.mouse_hover {
host = mouse_hover.with_child(host, ctx).into_widget(ctx);
host = mouse_hover.with_child(host).into_widget();
}
if let Some(pointer_pressed) = self.pointer_pressed {
host = pointer_pressed
.with_child(host, ctx)
.into_widget(ctx);
host = pointer_pressed.with_child(host).into_widget();
}
if let Some(fitted_box) = self.fitted_box {
host = fitted_box.with_child(host, ctx).into_widget(ctx);
host = fitted_box.with_child(host).into_widget();
}
if let Some(box_decoration) = self.box_decoration {
host = box_decoration
.with_child(host, ctx)
.into_widget(ctx);
host = box_decoration.with_child(host).into_widget();
}
if let Some(padding) = self.padding {
host = padding.with_child(host, ctx).into_widget(ctx);
host = padding.with_child(host).into_widget();
}
if let Some(layout_box) = self.layout_box {
host = layout_box.with_child(host, ctx).into_widget(ctx);
host = layout_box.with_child(host).into_widget();
}
if let Some(cursor) = self.cursor {
host = cursor.with_child(host, ctx).into_widget(ctx);
host = cursor.with_child(host).into_widget();
}
if let Some(margin) = self.margin {
host = margin.with_child(host, ctx).into_widget(ctx);
host = margin.with_child(host).into_widget();
}
if let Some(scrollable) = self.scrollable {
host = scrollable.with_child(host, ctx).into_widget(ctx);
host = scrollable.with_child(host).into_widget();
}
if let Some(transform) = self.transform {
host = transform.with_child(host, ctx).into_widget(ctx);
host = transform.with_child(host).into_widget();
}
if let Some(h_align) = self.h_align {
host = h_align.with_child(host, ctx).into_widget(ctx);
host = h_align.with_child(host).into_widget();
}
if let Some(v_align) = self.v_align {
host = v_align.with_child(host, ctx).into_widget(ctx);
host = v_align.with_child(host).into_widget();
}
if let Some(relative_anchor) = self.relative_anchor {
host = relative_anchor
.with_child(host, ctx)
.into_widget(ctx);
host = relative_anchor.with_child(host).into_widget();
}
if let Some(global_anchor) = self.global_anchor {
host = global_anchor
.with_child(host, ctx)
.into_widget(ctx);
host = global_anchor.with_child(host).into_widget();
}
if let Some(visibility) = self.visibility {
host = visibility.with_child(host, ctx).into_widget(ctx);
host = visibility.with_child(host).into_widget();
}
if let Some(opacity) = self.opacity {
host = opacity.with_child(host, ctx).into_widget(ctx);
host = opacity.with_child(host).into_widget();
}
if let Some(keep_alive) = self.keep_alive {
host = keep_alive.with_child(host, ctx).into_widget(ctx);
host = keep_alive.with_child(host).into_widget();
}
if let Some(h) = self.keep_alive_unsubscribe_handle {
let arena = &mut ctx.tree.borrow_mut().arena;
host.id().attach_anonymous_data(h, arena);
host = host.attach_anonymous_data(h);
}
self.id.set(host.id());
let host = self.id.clone().bind(host);
host
}
}

impl FatObj<()> {
#[inline]
#[track_caller]
pub fn with_child<C>(self, child: C, _: &BuildCtx) -> FatObj<C> { self.map(move |_| child) }
pub fn with_child<C>(self, child: C) -> FatObj<C> { self.map(move |_| child) }
}

impl<T> std::ops::Deref for FatObj<T> {
Expand Down
20 changes: 10 additions & 10 deletions core/src/builtin_widgets/align.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,47 +168,47 @@ mod tests {
const CHILD_SIZE: Size = Size::new(10., 10.);
const WND_SIZE: Size = Size::new(100., 100.);

fn h_align(h_align: HAlign) -> impl IntoWidgetStrict<FN> {
fn h_align(h_align: HAlign) -> impl IntoWidget<'static, FN> {
fn_widget! {
@HAlignWidget {
h_align,
@MockBox { size: CHILD_SIZE }
}
}
}
fn left_align() -> impl IntoWidgetStrict<FN> { h_align(HAlign::Left) }
fn left_align() -> impl IntoWidget<'static, FN> { h_align(HAlign::Left) }
widget_layout_test!(
left_align,
wnd_size = WND_SIZE,
{ path = [0], width == 100., height == 10.,}
{ path = [0, 0], size == CHILD_SIZE, }
);

fn h_center_align() -> impl IntoWidgetStrict<FN> { h_align(HAlign::Center) }
fn h_center_align() -> impl IntoWidget<'static, FN> { h_align(HAlign::Center) }
widget_layout_test!(
h_center_align,
wnd_size = WND_SIZE,
{ path = [0], width == 100., height == 10.,}
{ path = [0, 0], x == 45., size == CHILD_SIZE,}
);

fn right_align() -> impl IntoWidgetStrict<FN> { h_align(HAlign::Right) }
fn right_align() -> impl IntoWidget<'static, FN> { h_align(HAlign::Right) }
widget_layout_test!(
right_align,
wnd_size = WND_SIZE,
{ path = [0], width == 100., height == 10.,}
{ path = [0, 0], x == 90., size == CHILD_SIZE,}
);

fn h_stretch_algin() -> impl IntoWidgetStrict<FN> { h_align(HAlign::Stretch) }
fn h_stretch_algin() -> impl IntoWidget<'static, FN> { h_align(HAlign::Stretch) }
widget_layout_test!(
h_stretch_algin,
wnd_size = WND_SIZE,
{ path = [0], width == 100., height == 10.,}
{ path = [0, 0], x == 0., width == 100., height == 10.,}
);

fn v_align(v_align: VAlign) -> impl IntoWidgetStrict<FN> {
fn v_align(v_align: VAlign) -> impl IntoWidget<'static, FN> {
fn_widget! {
@VAlignWidget {
v_align,
Expand All @@ -217,31 +217,31 @@ mod tests {
}
}

fn top_align() -> impl IntoWidgetStrict<FN> { v_align(VAlign::Top) }
fn top_align() -> impl IntoWidget<'static, FN> { v_align(VAlign::Top) }
widget_layout_test!(
top_align,
wnd_size = WND_SIZE,
{ path = [0], width == 10., height == 100.,}
{ path = [0, 0], size == CHILD_SIZE,}
);

fn v_center_align() -> impl IntoWidgetStrict<FN> { v_align(VAlign::Center) }
fn v_center_align() -> impl IntoWidget<'static, FN> { v_align(VAlign::Center) }
widget_layout_test!(
v_center_align,
wnd_size = WND_SIZE,
{ path = [0], width == 10., height == 100.,}
{ path = [0, 0], y == 45., size == CHILD_SIZE,}
);

fn bottom_align() -> impl IntoWidgetStrict<FN> { v_align(VAlign::Bottom) }
fn bottom_align() -> impl IntoWidget<'static, FN> { v_align(VAlign::Bottom) }
widget_layout_test!(
bottom_align,
wnd_size = WND_SIZE,
{ path = [0], width == 10., height == 100.,}
{ path = [0, 0], y == 90., size == CHILD_SIZE,}
);

fn v_stretch_align() -> impl IntoWidgetStrict<FN> { v_align(VAlign::Stretch) }
fn v_stretch_align() -> impl IntoWidget<'static, FN> { v_align(VAlign::Stretch) }
widget_layout_test!(
v_stretch_align,
wnd_size = WND_SIZE,
Expand Down
8 changes: 4 additions & 4 deletions core/src/builtin_widgets/anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ mod test {
const CHILD_SIZE: Size = Size::new(50., 50.);
const WND_SIZE: Size = Size::new(100., 100.);

fn pixel_left_top() -> impl IntoWidgetStrict<FN> {
fn pixel_left_top() -> impl IntoWidget<'static, FN> {
fn_widget! {
@MockBox {
size: CHILD_SIZE,
Expand All @@ -220,7 +220,7 @@ mod test {
{ path = [0, 0], x == 1., }
);

fn pixel_left_bottom() -> impl IntoWidgetStrict<FN> {
fn pixel_left_bottom() -> impl IntoWidget<'static, FN> {
fn_widget! {
@MockBox {
size: CHILD_SIZE,
Expand All @@ -235,7 +235,7 @@ mod test {
{ path = [0, 0], x == 1., }
);

fn pixel_top_right() -> impl IntoWidgetStrict<FN> {
fn pixel_top_right() -> impl IntoWidget<'static, FN> {
fn_widget! {
@MockBox {
size: CHILD_SIZE,
Expand All @@ -250,7 +250,7 @@ mod test {
{ path = [0, 0], x == 49.,}
);

fn pixel_bottom_right() -> impl IntoWidgetStrict<FN> {
fn pixel_bottom_right() -> impl IntoWidget<'static, FN> {
fn_widget! {
@MockBox {
size: CHILD_SIZE,
Expand Down
2 changes: 1 addition & 1 deletion core/src/builtin_widgets/box_decoration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ mod tests {
}

const SIZE: Size = Size::new(100., 100.);
fn with_border() -> impl IntoWidgetStrict<FN> {
fn with_border() -> impl IntoWidget<'static, FN> {
fn_widget! {
@MockBox {
size: SIZE,
Expand Down
2 changes: 1 addition & 1 deletion core/src/builtin_widgets/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ mod tests {
use crate::test_helper::*;

const SIZE: Size = Size::new(100., 100.);
fn smoke() -> impl IntoWidgetStrict<FN> {
fn smoke() -> impl IntoWidget<'static, FN> {
fn_widget! { @Container { size: SIZE }}
}
widget_layout_test!(smoke, size == SIZE,);
Expand Down
7 changes: 4 additions & 3 deletions core/src/builtin_widgets/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ impl Declare for Cursor {
fn declarer() -> Self::Builder { FatObj::new(()) }
}

impl ComposeChild for Cursor {
type Child = Widget;
fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> impl IntoWidgetStrict<FN> {
impl<'c> ComposeChild<'c> for Cursor {
type Child = Widget<'c>;
fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> Widget<'c> {
fn_widget! {
let save_cursor: Stateful<Option<CursorIcon>> = Stateful::new(None);
@$child {
Expand All @@ -40,6 +40,7 @@ impl ComposeChild for Cursor {
},
}
}
.into_widget()
}
}

Expand Down
2 changes: 1 addition & 1 deletion core/src/builtin_widgets/fitted_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ mod tests {
.test();
}

fn as_builtin_field() -> impl IntoWidgetStrict<FN> {
fn as_builtin_field() -> impl IntoWidget<'static, FN> {
fn_widget! {
@MockBox {
size: Size::new(200., 200.),
Expand Down
Loading

0 comments on commit 42f38c2

Please sign in to comment.