Skip to content

Have TestHarness methods use WidgetTag instead of WidgetId#1738

Open
PoignardAzur wants to merge 4 commits into
linebender:mainfrom
PoignardAzur:widget_id_uses
Open

Have TestHarness methods use WidgetTag instead of WidgetId#1738
PoignardAzur wants to merge 4 commits into
linebender:mainfrom
PoignardAzur:widget_id_uses

Conversation

@PoignardAzur
Copy link
Copy Markdown
Contributor

@PoignardAzur PoignardAzur commented Apr 13, 2026

This is part of an effort to make WidgetTag the main primitive used in tests.

Part of this was generated with Claude Code, and then I rewrote almost everything by hand anyway.

Copy link
Copy Markdown
Member

@xStrom xStrom left a comment

Choose a reason for hiding this comment

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

The changes look fine for the specified goal. However, could you say a few words about why we would want to do this? It's not obvious to me why the tags are better.

Comment thread masonry_core/src/app/render_root.rs
Copy link
Copy Markdown
Contributor

@waywardmonkeys waywardmonkeys left a comment

Choose a reason for hiding this comment

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

Also, what is the lifecycle on the dynamically created tags (RenderRoot::make_tag_for_widget)? Is this just for tests? (but it is available in a way that people will call it?)

Comment thread masonry_testing/src/harness.rs Outdated

for (&id, &next_id) in std::iter::zip(&focusable_widgets, &next_focusable_widgets) {
harness.focus_on(Some(id));
let tag = harness.make_dyn_tag_for_widget(id);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This isn't great.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You're subverting the point of what you're doing here that is ...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I agree that this isn't very elegant, but also this test isn't really representative of how the API would usually be used.

As I mentioned in another comment, my ideal split is "WidgetTag when you know the widget before adding it, WidgetId when you know the widget after it's added".

While in most tests we know our tree ahead of time and WidgetTag is more convenient, there's always a few corner cases where we'd really want WidgetId instead. Which means we have three choices:

  • 1: Duplicate every API to accept either WidgetTag or WidgetId.
  • 2: Use an escape hatch to get a WidgetId from a WidgetTag.
  • 3: Use an escape hatch to get a WidgetTag from a WidgetId.

Existing code does a mix of 1 and 2, this PR moves towards 3. The result is overall much less verbose, except for this specific code snippet.

@PoignardAzur
Copy link
Copy Markdown
Contributor Author

The changes look fine for the specified goal. However, could you say a few words about why we would want to do this? It's not obvious to me why the tags are better.

There's two reasons:

  • Since some TestHarness methods already require WidgetTag, I'd rather have all of them require WidgetTag and have a unified API.
  • On the long term, I want to remove WidgetId from NewWidget and make it so a widget only gets an id once it's added to the widget tree. This is easier to do if none of the tests use WidgetId.

Also, what is the lifecycle on the dynamically created tags (RenderRoot::make_tag_for_widget)? Is this just for tests? (but it is available in a way that people will call it?)

In general, WidgetTags aren't garbage collected, so the lifecycle is "until the TestHarness/RenderRoot is dropped".

This is fine because WidgetTags are mostly used in tests and example code and not in e.g. Xilem.

@xStrom
Copy link
Copy Markdown
Member

xStrom commented Apr 15, 2026

Example code tends to end up as production code. So more generally speaking, if we're showing people that identifying widgets should happen with tags, then that will be the canonical production way. Which means it isn't great if they're not garbage collected at all.

What's your vision for production code (i.e. apps that use Masonry) that needs to identify widgets. Is it WidgetId or WidgetTag?

@PoignardAzur
Copy link
Copy Markdown
Contributor Author

Example code tends to end up as production code. [...] Which means it isn't great if they're not garbage collected at all.

Fair enough, though I'll note this PR doesn't touch example code, and the problem already exists on main.

If you think this is pressing, I'd rather add a TODO comment and implement garbage collection in a separate PR.

What's your vision for production code (i.e. apps that use Masonry) that needs to identify widgets. Is it WidgetId or WidgetTag?

Overall, my vision is "WidgetTag if you know which widget you want before it's added to the widget tree, WidgetId if you want to refer to a widget which is already in the tree".

Unit tests will always "know" their tree ahead of time, hence why I want TestHarness APIs to use WidgetTag as much possible.

@xStrom
Copy link
Copy Markdown
Member

xStrom commented Apr 19, 2026

No I don't think the garbage collection needs to happen in this PR, but if we're moving towards tags being the primary recommendation then I think it makes sense for them to become better in a bunch of ways.

One thing that worries me is that tags will just be a level of indirection. You've talked before about taking total control over ids so that a slotmap could be used. I can see the performance benefit of that idea. However, now even if the widget owner knows the id, we would recommend using a tag instead? That seems to be the goal here, especially given your description of this PR moving towards the third path of "3: Use an escape hatch to get a WidgetTag from a WidgetId". So the owner already knows the id, that id would give fast slotmap access, but instead the owner should get some sort of tag created, just so the tag can be converted to the id again. Seems like a lot of busywork for no gain.

Also, to be clear, I'm not so much worried about what the test harness does. I'm more so thinking about production usage and whether the overall shift towards tags provides any significant value for the extra indirection cost.

@PoignardAzur
Copy link
Copy Markdown
Contributor Author

However, now even if the widget owner knows the id, we would recommend using a tag instead? That seems to be the goal here, especially given your description of this PR moving towards the third path of "3: Use an escape hatch to get a WidgetTag from a WidgetId".

You'll notice that this PR changes TestHarness APIs to mostly use WidgetTag, but the matching RenderRoot APIs still mostly use WidgetId, and I do not expect that to change.

(The RenderRoot APIs that currently use WidgetTag are the ones where WidgetTag's strong typing lets you skip downcasting, which is really nice for keeping example code concise.)

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.

3 participants