Sol on Immediate Mode GUIs (IMGUI)

04 - GEN_ID

Source code for this chapter

Luckily, the widget ID "problem" is relatively easy to solve.

// If you're going to render widgets to the same
// UI from different source files, you can avoid
// ID collisions by defining IMGUI_SRC_ID before
// this define block:
#ifdef IMGUI_SRC_ID
#define GEN_ID ((IMGUI_SRC_ID) + (__LINE__))
#else
#define GEN_ID (__LINE__)
#endif

The above macro(s) result in unique IDs for the widgets, as long as no more than one widget is called per source line.

Since the widget rendering code may span several source files, some ID collisions may occur. This can be solved by including a #define IMGUI_SRC_ID in one (or all) of the offending source files, and give it a relatively high (unique) value.

I considered different kinds of preprocessor macros, like FILE, as the source code id, but these are not so much fun to play with (especially since the compilers tend to give the full path of the source file with that macro). Due to the nature of IMGUI, these can't be preprocessed either, and would have to be parsed every time the widget is called.

So, since most of the obvious solutions have pretty much the same limitations while being more complicated to implement, the IMGUI_SRC_ID will do fine. If you're getting collisions, just change one define and you should be fine.

To try this out, the button calls in render change to:

button(GEN_ID,50,50);
  
  button(GEN_ID,150,50);
  
  if (button(GEN_ID,50,150))
    bgcolor = (SDL_GetTicks() * 0xc0cac01a) | 0x77;
  
  if (button(GEN_ID,150,150))
    exit(0);

Should you need to know, outside the widget, whether a certain widget is hot or active, you can always store the GEN_ID value to a variable before using it (as long as there's only one GEN_ID per line).

Also, if you're generating widgets in a loop, you will need to generate the ID:s in a different way, or all of the widgets will end up with the same IDs.

One hack that I came up with which would solve some of the problems would be to generate an unique string for each widget call, and then use the string's address as the unique id, like this:

#define STRINGERMACRO( x ) #x 
#define GEN_ID (int)(__FILE__ STRINGERMACRO(__LINE__))

This will solve the ID collisions as long as you only have one widget per line. It won't solve any other problems, like when you're creating widgets in a for loop. Additionally it will add a lot of unused static data to your application. Hence, I like the simpler solution better.

Let's look at a slightly more complicated widget next - a scroll bar.

Next: 05 - Scrollbar

Any comments etc. can be emailed to me.