Sol's Graphics for Beginners

31 - IMGUI

IMGUI stands for Immediate Mode GUI. This is a rather large topic, and as such I've written a separate tutorial about it. This chapter covers plugging in a simple IMGUI with a couple widgets that we'll need with sparse explanations; please read the IMGUI tutorial before going through this chapter.

Read the IMGUI tutorial here

Most of the new code in this chapter goes to a new file called imgui.cpp. Since it's mostly a copy from the IMGUI tutorial, I won't go through it in detail - you can download the source here.

In order to get this to compile, we'll need to add a bunch of stuff to gp.h. First, we have a new #define block. Add the following after all the other #define lines:

// 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

Please refer to the IMGUI tutorial for the significance of the GEN_ID macro.

Next, we have a new structure. Add the following after the other structures:

// UI state for the IMGUI
struct UIState
{
  int mMouseX;
  int mMouseY;
  int mMouseDown;

  int mHotItem;
  int mActiveItem;

  int mKbdItem;
  int mKeyEntered;
  int mKeyMod;
  
  int mLastWidget;
};

We're not going to need text input in this game, and thus the variable that would be needed to support text input is omitted.

Next, we need a new global variable. Add the following after the other extern lines:

// UI state for IMGUI
extern UIState gUIState;

We'll be adding the variable to main.cpp soon.

Finally, there's a block of new functions. Add these to the end of the file:

extern int imgui_button(int aId, int aX, int aY, int aW, int aH, const char *aText);
extern int imgui_slider(int aId, int aX, int aY, int aW, int aH, int aMax, int &aValue);
extern void imgui_begin();
extern void imgui_end();
extern void imgui_open();
extern void imgui_close();

Next we'll go to main.cpp and add a new global variable:

// UI state for IMGUI
UIState gUIState;

Finally, we need to adjust the main function to update the UI state according to the users' actions.

Add the following new cases to the event.type switch structure:

case SDL_MOUSEMOTION:
        // update mouse position
        gUIState.mMouseX = event.motion.x;
        gUIState.mMouseY = event.motion.y;
        break;
      case SDL_MOUSEBUTTONDOWN:
        // update button down state if left-clicking
        if (event.button.button == 1)
          gUIState.mMouseDown = 1;
        break;
      case SDL_MOUSEBUTTONUP:
        // update button down state if left-clicking
        if (event.button.button == 1)
          gUIState.mMouseDown = 0;
        break;

Additionally, add the following lines to the beginning of the SDL_KEYDOWN case:

// If a key is pressed, report it to the widgets
        gUIState.mKeyEntered = event.key.keysym.sym;
        gUIState.mKeyMod = event.key.keysym.mod;

We could do all of the above conditionally only for the cases where we're in a menu state, but the operations are so lightweight that the required comparisons would probably take more processing power. Alternatively we could have a completely different event loop for menu states, but I don't feel that to be neccessary at this point.

This chapter has been quite boring and unenlightning, but it will all become clearer once we get to actually use the widgets, and you won't have to wait long, since..

Next we'll put the IMGUI to work in the 32. Menus..

Having problems? Improvement ideas? Just want to discuss this tutorial? Try the forums!

Any comments etc. can be emailed to me.