Hacker News new | past | comments | ask | show | jobs | submit login

Every immediate-mode UI deals with the id namespace issue. What they usually have in common is a hierarchical id namespace where you have an id stack and a child id is derived from the parent id via hashing, child_id = hash(parent_id, widget_type, subid). [1] The subid can be derived from widget arguments that are likely to be unique and stable from frame to frame, e.g. a text edit box's buffer pointer. But there always needs to be a way to provide an explicit subid since the implicit method doesn't always cut it. Alternatively, as long as the default subid is stable you can always make it unique by wrapping the widget (and other nearby related widgets) with an explicit pair of push_id/pop_id calls. You can see an example here: https://github.com/ocornut/imgui/blob/master/imgui_demo.cpp#.... The defaults work most of the time but this is definitely an aspect of immediate-mode UIs that can't just be treated as a hidden implementation detail.

[1] The prevailing use of hashing is a consequence of the popular immediate-mode UI libraries being focused on minimal state per widget. If you already plan to maintain significant state for every widget (as you would need in an immediate-mode interface to win32 controls) you would just do hierarchical interning with sequential id assignment (i.e. the first time a new subid is used with a given parent, it is assigned a sequential global id and put in a table so the association can be memoized across frames) and then hash collisions won't cause id collisions.




Yeah, I essentially copied imgui's ID stack approach for my experiments too. (I've been using imgui for some other projects to great success.) It still seems like a hack; I'm quite surprised that no programming language (I'm aware of) makes it possible to uniquely identify callsites like that. Maybe it hints at a more general blind spot/free real estate in PL design :)

(On the off chance you're curious, I just pushed some previously unpushed updates to that experiment I had sitting around, so now it has labels and text entry too. I guess the real test of the architecture would still be making an alternative "rendering backend" based on win32 widgets or something.)


Focusing too much on the call site is probably misleading, which is why most production-quality immediate-mode UI libraries generally don't rely on __FILE__/__LINE or stack walks or anything else like that. The issue isn't just loops. As soon as you wrap code in a function for reuse, the proximate call site no longer has anything to do with the widget ID; in the extreme case, your entire immediate-mode UI is data driven and there's nothing in the code paths that indicate anything at all about the widget IDs.

The real hack is implicit IDs, not the ID stack (which is just a way of implementing a hierarchical namespace like file system paths or URLs). Implicit IDs just work 99% of the time and rarely require manual intervention, so seeking a 100% solution is a tempting siren song. But once you actually start writing UIs like this, the 99% solution is just fine.


> As soon as you wrap code in a function for reuse, the proximate call site no longer has anything to do with the widget ID.

That's a good point.

(a) hash the entire call stack (though that might produce false negatives, i.e. consider two UI elements that should be the same distinct?)?

(b) put the burden on the reusable function to mark itself as such by pushing/popping an identifier of its own call site on the ID stack?

> The real hack is implicit IDs, not the ID stack (which is just a way of implementing a hierarchical namespace like file system paths or URLs). The fact that implicit IDs just work 99% of the time and only require manual intervention 1% of the time is a false siren song into letting you believe a 100% solution is desirable (you have to consider the marginal cost of what it would entail).

Well, this is just the standard problem of library design, isn't it? You always have to figure out the appropriate tradeoff between supporting rare cases and making common ones easy. (Of course, you can often do both; in this case, you probably could both give "explicit ID" and "call site ID" versions of each UI element API.)


Yeah, a mixture of implicit and explicit IDs is fine and what everyone does in one form or another. That's what I had in mind as the 99% solution. You don't want the default implicit ID scheme to be too clever or opaque so the programmer can easily diagnose what went wrong when it inevitably does. I was just cautioning against relying too much on ever fancier implicit schemes because of those failure modes; I went down that path a few times when I first started experimenting with immediate-mode UIs years ago.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: