Sol on Immediate Mode GUIs (IMGUI)

02 - UI State

Source code for this chapter

When you look at all the widgets in the world, you may find that they can be implemented with a relatively small set of global data. Ignoring keyboard IO for now, all you pretty much need can be described with the following structure:

struct UIState
{
  int mousex;
  int mousey;
  int mousedown;

  int hotitem;
  int activeitem;
} 
uistate = {0,0,0,0,0};

For those less familiar with the C syntax, the above defines a struct called UIState, and also a global variable 'uistate' of the type UIState, and sets the initial values of the members to zero.

The above data could be easily put into a singleton for a slightly more OOP approach, or left as separate global variables for even less OOP approach. =)

The mouse position and button states should be fairly easy to grasp. The two remaining variables store the currently hot and currently active item IDs.

Hot item is the one that's below the mouse cursor. If you move the mouse cursor on top of a button, for example, it becomes 'hot'.

Active item is the one you're currently interacting with. A button becomes 'active' when you press the mouse button on it. If you keep the button pressed and drag the mouse from the button, the button remains 'active', even though it loses it's 'hot' state. More on this when we actually implement a button.

To make the above structure useful, the event loop in main function changes to:

switch (event.type) 
{
case SDL_MOUSEMOTION:
  // update mouse position
  uistate.mousex = event.motion.x;
  uistate.mousey = event.motion.y;
  break;
case SDL_MOUSEBUTTONDOWN:
  // update button down state if left-clicking
  if (event.button.button == 1)
    uistate.mousedown = 1;
  break;
case SDL_MOUSEBUTTONUP:
  // update button down state if left-clicking
  if (event.button.button == 1)
    uistate.mousedown = 0;
  break;
case SDL_KEYUP:                  
  switch (event.key.keysym.sym)
  {
  case SDLK_ESCAPE:
    // If escape is pressed, return (and thus, quit)
    return 0;
  }
  break;
case SDL_QUIT:
  return(0);
}

The above, as may be guessed, updates the uistate global structure with the desired content whenever some event occurs. The similar binding should be relatively simple on different environments.

Finally, to test that the bindings work, the testing drawrect call in render changes to:

// test that the uistate is working
drawrect(uistate.mousex - 32,uistate.mousey - 24,
          64,48,0xff << (uistate.mousedown * 8));

When run, you should see that the test rectangle moves with the mouse, and changes color if a button is down.

SDL is nice enough to handle clipping etc. for us with the fillrect function.

Next, we'll use this functionality to create a button.

Next: 03 - Button

Any comments etc. can be emailed to me.