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

Provide a builder pattern interface to the construction layout tree #275

Open
Tracked by #345
giantpand2000 opened this issue Nov 30, 2022 · 7 comments
Open
Tracked by #345
Labels
enhancement New feature or request good first issue Good for newcomers usability Make the library more comfortable to use

Comments

@giantpand2000
Copy link

giantpand2000 commented Nov 30, 2022

What problem does this solve or what need does it fill?

Provides a more compact, write-friendly, ergonomic interface

What solution would you like?

@LayoutSpec
public class PlaygroundComponentSpec {
  @OnCreateLayout
  static Component onCreateLayout(ComponentContext c) {
    return Row.create(c)
      .child(
        Row.create(c)
          .widthDip(100)
          .heightDip(100))
      .child(
        Row.create(c)
          .widthDip(100)
          .heightDip(100)
          .marginDip(YogaEdge.HORIZONTAL, 20)
          .flexGrow(1))
      .child(
        Row.create(c)
          .widthDip(100)
          .heightDip(100))
      .widthDip(500)
      .heightDip(500)
      .alignItems(YogaAlign.FLEX_START)
      .paddingDip(YogaEdge.ALL, 20)
      .build();
  }
}

What alternative(s) have you considered?

Since many anonymous nodes are used, a method of result feedback is needed, such as having the node save a callback function, or a Trait that provides result feedback

Additional context

The sample code comes from the Yoga home page, a Java package called litho. But it can be translated quite directly to Rust code.

@giantpand2000 giantpand2000 added the enhancement New feature or request label Nov 30, 2022
@nicoburns nicoburns added the usability Make the library more comfortable to use label Nov 30, 2022
@alice-i-cecile
Copy link
Collaborator

I'm on board :) I think this is very useful when using this crate more directly.

@giantpand2000
Copy link
Author

Additions:

What alternative(s) have you considered?

Using NodeRef(s) might be a better way to get information about individual nodes from the tree builder. The idea comes from Yew's NodeRef. Both interior mutability and initialization tests are provided.

@nicoburns nicoburns added the good first issue Good for newcomers label Dec 30, 2022
@nicoburns
Copy link
Collaborator

Inspired by this discussion on Discord, I have come to the opinion that an API that automatically creates a Style::DEFAULT and then passes a mutable reference to the created style object into a closure might be particularly nice to use:

Something like:

Style::build(|style| {
    style.width = points(100.);
    style.height = points(200.);
});

where the style parameter is an &mut Style.
With setters on Style this could become:

Style::build(|style| style.width(100.).height(200.))

where the style parameter is an &mut Style.
And if trait a were used to accept a tuple of callbacks / types implementing a trait, than we could potentially create an API like the following (which would also allow the easier creation of user-defined style helpers):

Style::build((width(100.), height(200.))

@Weibye
Copy link
Collaborator

Weibye commented Jan 13, 2023

Style::build((width(100.), height(200.))

I really like this pattern!

@nicoburns nicoburns mentioned this issue Jan 30, 2023
37 tasks
@nicoburns
Copy link
Collaborator

I think something like the following would be quite easy to create:

let root_node = Style::column()
    .width(800)
    .height(100)
    .with_children(|tree| {
        Style::leaf().width(800).height(100).build(&mut tree);
        Style::leaf().width(800).flex_grow(1.0).build(&mut tree);
    })
    .build(&mut tree);

This is the same as the example in the README. It's quite an improvement!

@nicoburns
Copy link
Collaborator

A couple of other API idea that I think would be doable.

With nested builders, closures taking &mut Style and dedicated constructors for row/column/grid:

    let root_node_id = taffy.new_flex_column(
        |style| style.width(800).height(600),
        |cx| {
            cx.new_leaf(|style| style.height(100));
            cx.new_leaf(|style| style.flex_grow(1.0));
        },
    );

Using a macro (yoga-rs implements an API like this using a simple macro_rules macro):

    let root_node_id = taffy.new_flex_column(style! {width: 800 px, height: 600 px}, |cx| {
        cx.new_leaf(style! {height: 800 px});
        cx.new_leaf(style! {flex_grow: 1.0});
    });

@alice-i-cecile
Copy link
Collaborator

The builder approach is my favorite here stylistically :) Would like to avoid macros if possible, and closures can be rough for beginners.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers usability Make the library more comfortable to use
Projects
Status: Todo
Development

No branches or pull requests

4 participants