1. Computing

An SDL GUI for Empire Tutorial Four

More Controls

By

Screenshot of Empire Map with TTF text

This is another tutorial about developing the Empire game. If you've found this article first, please see part one. Here's more information about the Empire Game.

In the previous SDL Gui tutorial 3 I started using the SDL_ttf True Type Fonts in the Empire game.

  • Download Source Code File (Zip).
  • Download binaries (All Exe + SDL dlls + graphic files etc) zipped up. Just unzip everything into one folder. It should run. You shouldn't need SDL installed unless you are developing it.

This tutorial is about improving the look of the SDL GUI controls, including making the button animate and adding new controls.

First though I played with a few more free fonts until I found GUNPLAY font which seemed not a bad choice. Watch out for the underscore in the filename.

Improving The Button

As it stand, it's a bit flat, doesn't respond in any way so it needs sprucing up. The easiest way apart from giving it borders is to make it respond visually when clicked. The entire GUI is being redrawn about 60 times per second so for say 25 of those, following the click, the button text should be rendered a little to the right and down, say 2 pixels in each direction. If we go back to sdlgui.h we can see a button is defined like this:

struct sdlbutton {
  sdlbase;
  pchar labeltext;
  int labelsize;
  int labelcolor;
  int isDown;
  int countdown;
};

So by adding a couple of fields, one for the state int isDown and a countdown time then it's possible to do the following sequence of events when it's clicked.

  1. Set isDown= 1 and countdown= 25
  2. RenderButton

Every time the RenderButton() function is called, while isDown >0, do a countdown-- and when countdown reaches 0, set isDown to 0. RenderButton displays the button at 2,2 relative to its normal position while isDown >0. This gives the impression of clicking a button.

However as it stands, clicks are routed directly to the target, so we have no easy way of setting isDown to 1 etc. To do this we need to add a new event handler to the sdlbase macro. This preClick handler lets us do something before the control is clicked. So I've added this extra event handler:

void (*pPreClick)(struct sdlcontrol * self);\

In the initsdlguilib() and addcontrol() functions, this field is set to NULL. Then in addbutton, we assign a function called buttonPreclick to it.

void buttonPreclick(psdlcontrol self) {
  struct sdlbutton * pb= (struct sdlbutton *)self;
  pb->isDown = 1;
  pb->countDown = 15;
}

This declares a pointer pb that points to the generic sdlcontrol and casts it to a button and gives access to the two new fields. Next in CheckControlClickAt, the code to call Click is modified to call PreClick if it has a value.

if (control-> pPreClick)
  control-> pPreClick(control);
if (control-> pClick)
  control-> pClick(control);
return;

This ensures that buttonPreClick will be called when the button is clicked. Now all it needs is to modify Renderbutton to use these two variables.

Adding Boxes

SDL doesn't come with line drawing code so you have to code it yourself or use a library like SDL_gfx. I found some code where fast vertical and horizontal drawing was done and used that code. The two functions draw_vl and draw_hl do this. Basically they are plotting pixels quickly and working in a screen format independent way.

I've added these to the RenderButton code so it draws a rectangle around the button. And when the button is pressed, the top and left edge colors are swapped with the bottom and right and all four lines drawn 2 to the right and down. This gives the illusion that the button is pressed in.

Note this "animation" effect only happens when you click the button and holding the mouse button down does not keep the button pressed in. It pops up 20/60th of a second after clicking or whatever value is defined for the countDown in buttonPreclick().

Still I'm very pleased with it. The SDL Button now looks professional enough for me!

Adding a CheckBox Control

I've reused this line drawing in the RenderCheckbox function call to draw a 16 x 16 pixel box before the text and if self->isChecked is non zero, it draws an x in the box.

With C# Winforms in the past and Delphi before it, there's always been the possibility of recursion with checkbox controls.

To avoid this I added a PreClick event which is hard coded to call CheckBoxPreClick(). This toggles the value of the isChecked, so when the click event is called, self->isChecked has the post click toggled value. If you click an unchecked checkbox, it gets checked and in the Click event handler self->isChecked is 1. The CheckBoxPreClick just xors isChecked with 1 to toggle it.

Notes

The Color problem. There are two color types in use:

  1. SDL_Color
  2. unsigned int (Uint32)

The built in SDL_MapRGB() function returns a Uint32 which is not much good in functions like TTF_RenderText_Solid which expects a SDL_Color. This is a struct laid out like this:

typedef struct SDL_Color {
  Uint8 r;
  Uint8 g;
  Uint8 b;
  Uint8 unused;
} SDL_Color;

To simplify things I created two sets of colors. Those used in text printing have a t on the front twhite, tred etc and are created a simple assignment like this:

SDL_Color tred = { 255, 0, 0 };

But the colors used for fills, lines etc are unsigned ints and created by calls to SDL_MapRGB. It's not ideal and I may revise it to create code that converts unsigned ints to SDL_Color.

Conclusion

It's a work in progress but it's coming along with labels, buttons and checkboxes now and animation on the checkboxes and buttons.

In the next version I might modify the button background colors. It's currently the same as the panel which uses a global variable background which is not ideal. Plus the number of compiler warnings continues to grow which is never good. Most may not be actionable, but they create noise which may hide a genuine serious warning.

  1. About.com
  2. Computing
  3. C / C++ / C#
  4. Programming Games
  5. An SDL GUI for Empire Tutorial Four

©2014 About.com. All rights reserved.