PC Games
• Orb Tutorials
• 2D shoot 'em up Latest Updates
SDL2 Versus game tutorial
Download keys for SDL2 tutorials on itch.io
The Legend of Edgar 1.37
SDL2 Santa game tutorial 🎅
SDL2 Shooter 3 tutorial
Tags • android (3) • battle-for-the-solar-system (10) • blob-wars (10) • brexit (1) • code (6) • edgar (9) • games (43) • lasagne-monsters (1) • making-of (5) • match3 (1) • numberblocksonline (1) • orb (2) • site (1) • tanx (4) • three-guys (3) • three-guys-apocalypse (3) • tutorials (17) • water-closet (4) Books Firmware HF-Tech's chips have changed the world. Embedded into the heads of over 90% of the world's population, they have cured autism, dementia, provided intelligence boosts, and helped to ease some of the more mundane tasks in life. Daniel Blair, hacker and Workshop member, however is not convinced that everything is as rosy as it seems. But is he looking in all the wrong places..? |
— Creating a basic widget system — Note: this tutorial assumes knowledge of C, as well as prior tutorials.
Introduction Before we delve deeper into creating widgets, we should look into loading widgets from a file. This will make certain aspects of our widget building process easier, as you will come to see. For this, we'll simply be creating a file called data/widgets/title.json, which will contain our important widget details. Extract the archive, run cmake CMakeLists.txt, followed by make, and then use ./widgets03 to run the code. You will see a window open like the one above. Use the Up and Down arrows on you keyboard to change the highlighted menu option. Press Space or Return on the highlighted menu option to action it. When you're done, either select Exit or close the window. Inspecting the code We're now defining our widgets in a JSON file called title.json. We're going to be doing this for the remaining tutorials parts, so it's best to know what to expect. Looking at the JSON file, we don't see anything out of the ordinary: [ { "type" : "WT_BUTTON", "name" : "start", "x" : 0, "y" : 200, "label" : "Start" }, { "type" : "WT_BUTTON", "name" : "load", "x" : 0, "y" : 250, "label" : "Load Game" }, { "type" : "WT_BUTTON", "name" : "options", "x" : 0, "y" : 300, "label" : "Options" }, { "type" : "WT_BUTTON", "name" : "credits", "x" : 0, "y" : 350, "label" : "Credits" }, { "type" : "WT_BUTTON", "name" : "exit", "x" : 0, "y" : 400, "label" : "Exit" } ] It's a JSON array, holding a bunch of objects. Those are the widgets we were originally setting up in the initDemo function. We'll be loading them in widgets.c from now on. Pay attention to the type field. It's a string. What we'll be doing is mapping the string to a number when we load the widgets. Let's look at defs.h, where we've set up an enum to handle our types:
WT stands for Widget Type. We could've called it just BUTTON, but it's a good idea to prefix these things, in case there is a conflict with another enum later on during development. All our major changes have happened in widgets.c, so let's take a look, starting with initWidgets:
We're now making a call to a new function called loadWidgets, passing in the name of the file we want to load our widgets from. loadWidgets itself is a simple function:
We're loading our file data by calling our readFile function (defined in util.c) and passing in the filename. We're then using cJSON_Parse to convert the text into a JSON object, with which to work. With that done, we're looping through our JSON array, and calling createWidget on each object. Once we're done, we're deleting our JSON object and the text we loaded, to free the memory. createWidget is an important new function, as it's essentially our widget factory. Note that this function replaces our original createWidget function, where we would pass in the name of the widget to create. It's also static within widgets.c, making it private. It does inherit some of the original code, however, as we'll see:
The first thing we'll do is find out what type of widget we want to create. We do this by extracting the "type" field from our JSON object and passing it to a function called getWidgetType. If this function doesn't return -1, we'll know it's a valid type and move onto creating the widget. Looking at the JSON data at the top of the tutorial, we'll see that this is just WT_BUTTON right now. We'll see more on this function in a bit. Continuing with creating our widget: as before, we're setting up it's next and prev variables, to act as a double-linked list. After that, we want to extract our data from the JSON object we've passed into the function. Setting the name and label of the Widget is as simple as using our STRCPY macro to copy the char data. Next, we're setting the Widget's x and y values to those defined in the JSON object. With the base widget data fetched, we can now move onto setting up the data for each widget type. Again, we've only got one widget type right now, we our switch statement for handling the type only deals with WT_BUTTON. This will be expanded our in future, as we add more widget types. That's our createWidget function done, so we can move onto the other functions we've added. There's not a lot to getWidgetType, as we'll see:
We're just performing a strcmp on the type string that is passed into the function and returning a matching enum. Again, we're only supporting WT_BUTTON right now, so there isn't a lot more to it. If we don't recognise the widget, print a warning and return -1. This will cause our createWidget function to skip building the widget. Our other function is createButtonWidget. This is where we'll do some extra work to setup additonally widget details:
Not a lot going on - we're calling our calcTextDimensions function (defined in text.c) to calculate the size of the widget, using its label. Knowing the size of our widget can help us later with layout (as we'll see when we come to using it in demo.c). The final function we should look at is getWidget. This function is important to us, as we'll need a way to find the widgets that were loaded:
To find our Widget, we're just going to loop through our list of widgets and return the one with a matching name. If we don't find the widget, we'll print an error and return NULL. We've also made a minor change to drawWidgets:
Instead of drawing a right chevron, we're calling drawRect, and displaying a green square on the left-hand side of the active widget. The size of the square is being determined by the height of the widget itself. That's all our changes to widgets.c done. We now only have changes to demo.c to consider, as the rest of our work was restricted to widgets.c. The only change to demo.c was in the initDemo function:
Now, instead of calling createWidget (which is private in widgets.c) we're calling getWidget and passing in the name of the widget we want. For each of our widgets, we're adjusting the x value to centre it on screen. As our widget's width is known (it's w value, as setup in createButtonWidget), this is simple task of subtraction and division. And that's all we needed to do to load our widgets. Other than the adjustments in demo.c, everything else was done in widgets.c and is transparent to even the init functions of the main application. In our next part, we'll do something more interesting and look at creating a new type of widget. Purchase The source code for all parts of this tutorial (including assets) is available for purchase: From itch.io It is also available as part of the SDL2 tutorial bundle: |