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

Redesign template macro #14

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open

Conversation

NthTensor
Copy link
Collaborator

@NthTensor NthTensor commented Jan 9, 2025

I've redesigned several aspects of the template macro to incorporate community feedback.

Changes

  • The internals have been simplified. The confusing Prototype trait has been removed. Fragment no longer is generic over bundle type.
  • The Commands based api has been replaced with a more flexible EntityCommands version.
  • More things have been made pub.
  • The docs have been significantly improved.
  • The syntax of the macro itself has been streamlined to reduce boilerplate. Expressions are now used instead of blocks where possible, reducing the bracket and parenthesis clutter.
  • Bundles can now be dynamic using the new BoxedBundle trait and b!() macro. I've opted for b!(Foo) over bundle!(Foo) because it gets tedious to write.
  • Receipts are now stored in the ECS as components.
  • Applying templates to child entities created by other templates now works as expected.
  • A new example has been added.
  • Adds a new Callback component and on(into_observer_system) helper for adding observers to templates. Observers are added as children which always observe their parents.

This is a breaking change, as the syntax of the macro is not fully backwards compatible. Here is the full list of syntax changes:

  • A => symbol is now required between a fragment's bundle and it's list of children.
  • Bundles no longer need to be wrapped in blocks: {(Foo, Bar)} is now just (Foo, Bar).

Example

The proposed syntax changes:

// BEFORE
fn counter<T: Component>(num: usize, name: &str, inc: T, dec: T) -> Template {
    template! {
        { Text::new("You have ") }
        [
            { TextSpan::new(format!("{num}")) };
            { TextSpan::new(format!(" {name}!")) };
        ];
        {( Button, Text::new("Increase"), TextColor(css::GREEN.into()), inc, visible_if(num < 100) )};
        {( Button, Text::new("Decrease"), TextColor(css::RED.into()), dec, visible_if(num > 0) )};
    }
}

// AFTER
fn counter<T: Component>(num: usize, name: &str, inc: T, dec: T) -> Template {
    template! {
        Text::new("You have ") => [
            TextSpan::new(format!("{num}"));
            TextSpan::new(format!(" {name}!"));
        ];
        ( Button, Text::new("Increase"), TextColor(css::GREEN.into()), inc, visible_if(num < 100) );
        ( Button, Text::new("Decrease"), TextColor(css::RED.into()), dec, visible_if(num > 0) );
    }
}

Conditional bundles:

/// BEFORE
template !{
    @{
        if cond {
            template!{ name: {(ComponentA)}; } 
        } else {
            template!{ name: {(ComponentB, ComponentC)}; }
        }
    }
}

/// AFTER
template !{
    name: if cond { b!(ComponentA) } else { b!(ComponentB, ComponentC) };
}

Observers:

template!{
    Name::new("MyEntity") => [
        on(|trigger: Trigger<Pointer<Click>>| {
            // Do something when "MyEntity" is clicked.
        });
        on(|trigger: Trigger<Pointer<Drag>>| {
            // Do something when "MyEntity" is dragged.
        });
    ];
}

Other Notes

For increased compatibility with bsn, i'm considering swapping the ; for a ,.

.flatten()
.collect();

commands.entity(*scene_tree).build_children(template);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice how this is building a template on a child of another template. Now that receipts are stored on-entity, you can apply any template to any entity and it will just work (remove any components/children included in the previous template but not the new one).

README.md Outdated Show resolved Hide resolved
Co-authored-by: Oliver Maskery <omaskery@googlemail.com>
Copy link
Contributor

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember to update RELEASES.md :) I like these changes, but we should get ready to cut a new major version.

@NthTensor
Copy link
Collaborator Author

Yep yep. Just random question, how do we feel about a 1.0 release?

@alice-i-cecile
Copy link
Contributor

No strong feelings. I suppose this is about as 1.0 as we'll ever get?

Copy link

@villor villor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome evolution of this very handy macro! Loving the neater syntax and the b!-macro.

examples/scene_tree.rs Outdated Show resolved Hide resolved
examples/scene_tree.rs Outdated Show resolved Hide resolved
examples/scene_tree.rs Outdated Show resolved Hide resolved
src/template.rs Outdated Show resolved Hide resolved
src/template.rs Outdated Show resolved Hide resolved
Co-authored-by: Viktor Gustavsson <villor94@gmail.com>
@JMS55
Copy link

JMS55 commented Jan 9, 2025

I would hold off on a 1.0 for now. I'm planning to test this out in some editor stuff sometime soon hopefully, and might find additional stuff to change.

Copy link

@omaskery omaskery left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exciting!

Comment on lines +250 to +251
if let Some(parent_id) = world.get::<Parent>(entity_id).map(|parent| parent.get()) {
observer.watch_entity(parent_id);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this is just obvious to other people (I'm not super familiar with how observers work), but perhaps this could do with a comment briefly explaining why we observe the parent if it's there, and why we fallback to listening to all entities otherwise?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah, I'll be sure to document that. My thinking was, this could let you swap out different global observers by collecting them into a template.

Comment on lines 669 to 672
($fragments:ident; @ $block:block ; $( $($sib:tt)+ )? ) => {
$fragments.extend({ $block }); // Extend the fragments with the value of the block.
$(push_item!($fragments; $($sib)*))* // Continue pushing siblings onto the current list.
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
($fragments:ident; @ $block:block ; $( $($sib:tt)+ )? ) => {
$fragments.extend({ $block }); // Extend the fragments with the value of the block.
$(push_item!($fragments; $($sib)*))* // Continue pushing siblings onto the current list.
};
($fragments:ident; @ $splice:expr ; $( $($sib:tt)+ )? ) => {
$fragments.extend({ $splice }); // Extend the fragments with the result of the expression.
$(push_item!($fragments; $($sib)*))* // Continue pushing siblings onto the current list.
};

Maybe the brace-requirement could be removed for splices as well for consistency:

@counter(num_sheep, "sheep", Button::Increment, Button::Decrement);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to switch from the work account. Again...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered that, I was still on the fence.

@villor
Copy link

villor commented Jan 10, 2025

For increased compatibility with bsn, i'm considering swapping the ; for a ,.

I actually like that there is a separation between entity delimiters and component delimiters. Maybe BSN should do the same 🤷‍♂️

@NthTensor
Copy link
Collaborator Author

Same. Will keep then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants