Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor/simplify type convert #612

Merged
merged 6 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,34 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he

## [@Unreleased] - @ReleaseDate

### Features

- **core**: Introduced `IntoWidget` and `IntoChild`. (@M-Adoo #612)

The `IntoWidget` trait allows for the conversion of any widget to the type `Widget`.
The `IntoChild` trait provides a way to convert a more general type into a child of `ComposeChild`.

### Fixed

**core**: The generation of a pipe widget from another pipe widget may potentially result in a crash. (#612, @M-Adoo)

### Changed

- **core**: Lazy build the widget tree. (#612, @M-Adoo)
- **core**: Simplify the implementation of parent composition with child widgets. (#612, @M-Adoo)

Merge `SingleWithChild`, `MultiWithChild`, and `ComposeWithChild` into a single trait called WithChild.

### Breaking

- Removed `ChildFrom` and `FromAnother` traits (#612 @M-Adoo)
- Removed `SingleParent` and `MultiParent` traits. (#612 @M-Adoo)
- Removed `PairChild` and `PairWithChild` traits. User can use a generic type instead. (#612 @M-Adoo)
- Allow only the child to be converted to a widget or a type that implements the Into trait. (#612 @M-Adoo)
- Removed the all builder traits such as WidgetBuilder and ComposeBuilder and so on. (#612 @M-Adoo)
- All implicit child conversions have been removed, except for conversions to Widget. (#612 @M-Adoo)


## [0.4.0-alpha.3] - 2024-06-26

## [0.4.0-alpha.2] - 2024-06-19
Expand Down
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 WidgetBuilder {
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
145 changes: 59 additions & 86 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.))
/// .build(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
};

InnerWidget::LazyBuild(Box::new(f)).into()
}

pub fn assert_id(&self) -> WidgetId { self.0.get().unwrap() }
pub fn id(&self) -> Option<WidgetId> { self.0.get() }

fn set(&self, wid: WidgetId) { self.0.set(Some(wid)); }
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,138 +868,88 @@ impl<T> ObjDeclarer for FatObj<T> {
fn finish(self, _: &BuildCtx) -> Self::Target { self }
}

impl<T: SingleChild> SingleChild for FatObj<T> {}
impl<T: MultiChild> MultiChild for FatObj<T> {}

crate::widget::multi_build_replace_impl! {
impl<T: {#} > {#} for FatObj<T> {
#[track_caller]
fn build(self, ctx: &BuildCtx) -> Widget {
self.map(|host| host.build(ctx)).build(ctx)
}
}
impl<'w, T, const M: usize> IntoWidgetStrict<'w, M> for FatObj<T>
where
T: IntoWidget<'w, M>,
{
fn into_widget_strict(self) -> Widget<'w> { self.map(|w| w.into_widget()).compose() }
}

impl WidgetBuilder for FatObj<Widget> {
#[inline]
#[track_caller]
fn build(self, ctx: &BuildCtx) -> Widget {
impl<'a> FatObj<Widget<'a>> {
fn compose(self) -> Widget<'a> {
let mut host = self.host;
self.host_id.set(host.id());
host = self.host_id.clone().bind(host);
if let Some(mix_builtin) = self.mix_builtin {
host = mix_builtin.with_child(host, ctx).build(ctx)
host = mix_builtin.with_child(host).into_widget()
}
if let Some(request_focus) = self.request_focus {
host = request_focus.with_child(host, ctx).build(ctx);
host = request_focus.with_child(host).into_widget();
}
if let Some(has_focus) = self.has_focus {
host = has_focus.with_child(host, ctx).build(ctx);
host = has_focus.with_child(host).into_widget();
}
if let Some(mouse_hover) = self.mouse_hover {
host = mouse_hover.with_child(host, ctx).build(ctx);
host = mouse_hover.with_child(host).into_widget();
}
if let Some(pointer_pressed) = self.pointer_pressed {
host = pointer_pressed.with_child(host, ctx).build(ctx);
host = pointer_pressed.with_child(host).into_widget();
}
if let Some(fitted_box) = self.fitted_box {
host = fitted_box.with_child(host, ctx).build(ctx);
host = fitted_box.with_child(host).into_widget();
}
if let Some(box_decoration) = self.box_decoration {
host = box_decoration.with_child(host, ctx).build(ctx);
host = box_decoration.with_child(host).into_widget();
}
if let Some(padding) = self.padding {
host = padding.with_child(host, ctx).build(ctx);
host = padding.with_child(host).into_widget();
}
if let Some(layout_box) = self.layout_box {
host = layout_box.with_child(host, ctx).build(ctx);
host = layout_box.with_child(host).into_widget();
}
if let Some(cursor) = self.cursor {
host = cursor.with_child(host, ctx).build(ctx);
host = cursor.with_child(host).into_widget();
}
if let Some(margin) = self.margin {
host = margin.with_child(host, ctx).build(ctx);
host = margin.with_child(host).into_widget();
}
if let Some(scrollable) = self.scrollable {
host = scrollable.with_child(host, ctx).build(ctx);
host = scrollable.with_child(host).into_widget();
}
if let Some(transform) = self.transform {
host = transform.with_child(host, ctx).build(ctx);
host = transform.with_child(host).into_widget();
}
if let Some(h_align) = self.h_align {
host = h_align.with_child(host, ctx).build(ctx);
host = h_align.with_child(host).into_widget();
}
if let Some(v_align) = self.v_align {
host = v_align.with_child(host, ctx).build(ctx);
host = v_align.with_child(host).into_widget();
}
if let Some(relative_anchor) = self.relative_anchor {
host = relative_anchor.with_child(host, ctx).build(ctx);
host = relative_anchor.with_child(host).into_widget();
}
if let Some(global_anchor) = self.global_anchor {
host = global_anchor.with_child(host, ctx).build(ctx);
host = global_anchor.with_child(host).into_widget();
}
if let Some(visibility) = self.visibility {
host = visibility.with_child(host, ctx).build(ctx);
host = visibility.with_child(host).into_widget();
}
if let Some(opacity) = self.opacity {
host = opacity.with_child(host, ctx).build(ctx);
host = opacity.with_child(host).into_widget();
}
if let Some(keep_alive) = self.keep_alive {
host = keep_alive.with_child(host, ctx).build(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<T: ComposeWithChild<C, M>, C, M> ComposeWithChild<C, M> for FatObj<T> {
type Target = FatObj<T::Target>;

impl FatObj<()> {
#[inline]
#[track_caller]
fn with_child(self, child: C, ctx: &BuildCtx) -> Self::Target {
self.map(
#[cfg_attr(feature = "nightly", track_caller)]
|host| host.with_child(child, ctx),
)
}
}

impl<C> SingleWithChild<C, ()> for FatObj<()> {
type Target = FatObj<C>;

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

impl<T: PairWithChild<C>, C> PairWithChild<C> for FatObj<T> {
type Target = Pair<FatObj<T>, C>;

#[inline]
#[track_caller]
fn with_child(self, child: C, _: &BuildCtx) -> Self::Target { Pair::new(self, child) }
}

impl<T: SingleParent + 'static> SingleParent for FatObj<T> {
#[track_caller]
fn compose_child(self, child: Widget, ctx: &BuildCtx) -> Widget {
self
.map(|host| host.compose_child(child, ctx))
.build(ctx)
}
}

impl<T: MultiParent + 'static> MultiParent for FatObj<T> {
#[track_caller]
fn compose_children(self, children: impl Iterator<Item = Widget>, ctx: &BuildCtx) -> Widget {
self
.map(|host| host.compose_children(children, ctx))
.build(ctx)
}
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 WidgetBuilder {
fn h_align(h_align: HAlign) -> impl IntoWidget<'static, FN> {
fn_widget! {
@HAlignWidget {
h_align,
@MockBox { size: CHILD_SIZE }
}
}
}
fn left_align() -> impl WidgetBuilder { 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 WidgetBuilder { 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 WidgetBuilder { 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 WidgetBuilder { 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 WidgetBuilder {
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 WidgetBuilder { 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 WidgetBuilder { 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 WidgetBuilder { 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 WidgetBuilder { 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
Loading
Loading