Writing menus

I wanted to write an article on menus for a while now, and a post on Pete's QB Site finally gave me the motivation to do so. Thanks Knothead1008 (if that's your real name).

Menus are essentially a safe environment designed to allow users to modify variables and access specific parts of your program. All in all, items contained within each menu can only do three things: switch to another menu, trigger functions, and modify variables. How do we do that?

Building a simple menu

Let's start with something simple, like this code, which reads a bunch of items from DATA statements and displays them on the screen. It's nothing fancy (not even a menu), but it already contains two things we'll expand upon: a field dedicated to menu name and another for the item count; this can be used to attach a list of items to an identifiable menu. I'm sure you can see where this is going... what if we could store more menus, and switch from one to the other?

This next piece of code looks slightly more complex. This time, we can select the menu we want to display via {menuSeek}. A loop compares {menuSeek} and menu names found in DATA statements, and if the field doesn't match the variable, every item attached to that menu is skipped. When a matching field is found, the loop exits and every item is displayed. Well, it's easy for YOU to modify the code and get it to do what YOU want, but for the end-user it would be easier to have it done via the menu itself... so let's get to it.

First, the user needs to be able to change items. A new block is added to display and control the cursor with the UP and DOWN arrow (registered by INKEY$ as "H" and "P" respectively). This system is great because the boundaries are defined by {numItems} (the number of items contained within the selected menu); no need to create extra routines for each and every menu, all goes through the same pipeline and it works unirregardless (it's a new word I made up to replace "regardless", I hope you like it) of the selected menu.

Let's move to slightly more complex stuff: let's tie functions to items so we can actually interact with them using the space bar! This brings three major modifications to the code: first, we need to add a new field after each item so we can store a function string. Second, we have to store each item in an array for later use (in this example, the label is stored although, frankly, it's unnecessary). Finally, we create a new condition block that will parse the information contained within the new field when the space key is pressed. Here, a function string starting with ">" will switch to another menu (the rest of the string is the name of the menu we want to display), and a function string starting with "_" is used for special events. With these two things, you can already do a lot of things... but let's keep going.

Messing with variables

As you'd expect, variables are a big pain in the donkey because of how many things we need to account for (reading, displaying, modifying -including capping-, and writing). The "simple and dirty way" to do it (you know we won't do that because there's no hyperlink in this paragraph) would be to add another prefix and use it to display specific variables when items are being displayed: "=CONTINUES" would display the content of {cfgContinues} and "=SOUND" would display the content of {cfgSound}. We can add a bunch of conditional branching for handling and call it a day. I'd probably recommend that if you have very few settings the user can mess with. However, for larger menus it's going to be maintenance hell. So, what are the other options? Well... plenty.

In Sexy Smart Slide, I had no use for multiple variables to store random values because of how little room was needed. Instead, I relied on a simple 16-bit integer I called {flags} and whose purpose was to store... well, a bunch of bit flags. Multiple menu items used a starting offset (in bits) and computed the length (also in bits) that was required to store the various choices that could be picked. For instance, an item that went "SOUND: NO, YES" only needed 1 bit of information (because a bit can hold two values), and an item that went "BLOOD: NONE, SANE, SOME, A LOT, OVERKILL" would require 3 bits of information (because there are 5 choices and since 2 bits can only store 4 different values, we need an extra bit for the 5th - the number of bits can easily be obtained by rounding up the result of LOG({numChoices}) divided by LOG(2)). The code could write and read values from and to {flags} without any special handling, which was pretty cool! But what if we need more than 16 bits, what if we have larger numbers, or even strings!?

Let's create a shared variable with a simple user-defined type (UDT for short). The goal is to store all our settings at the same place and make it available globally so every function can access it. The coolest thing about using a UDT to store all your settings is that you can easily save it in a binary file (just write one variable to a file and you're done; loading is as easy). It means that while we're still bound to use that specific variable, we can in theory access each of its members individually without relying on scary branching by simply reading memory. We've got to be careful though: we must pay attention to the size of each member, how they should be processed, where they start, and not screw up by adding, removing, or moving around members in the UDT. The length of each member can be obtained with LEN(). Sadly, QuickBASIC can't retrieve the offset of each member for us, so we'll have to do that manually (the offset to a specific member is simply the total length (in bytes) of all the preceding members in the UDT; the first member is always at offset zero.)

We start by creating a routine that will return the offset, length and handling method for any given member (like explained above). After that, we can write a small chunk of code to properly display the content of the specified member. I don't know about you, but I think it's clean enough. This code allows us to handle different variables of the same type through the same pipeline, making things much easier to maintain. The other advantage is that you can now easily connect variables and text labels, which means you can save your configuration file as a text file that the user can modify in EDIT. Icing on the cake, adding new variables to the menu is piss-easy and only requires two new lines of code (that is, if the type of that variable is already coded).

Now, we stuff everything back together to obtain a much more complex code that can handle variables (strings, numbers, toggle), execute special actions, and switch from one section to another. It's primitive in some ways (no cap for integers - this can be added in the function string), and overly complex in others (the reading-from-memory-and-converting-content-back-and-forth part). All in all, it really depends on how many things your menu has to do.

Conclusion

There you have it, a working menu system. It's not optimized nor is it fool-proof, but it covers enough things to give you an idea of what to do next (optimize redrawing time, creating scrolling menus when too many items are being displayed at once, adding an actual graphic interface,... it really depends on your needs). With little imagination, you could even use the variable changing system to directly access {menuSeek}, thus removing the need of the ">" prefix. Why not split the function string and prefixes? Replace the BINARY type with a full-fleshed combo list (Sexy Smart Slide does it)? Why not drop the DATA instruction set altogether and replace it with two different UDT, an array for menus and another for items? And when that's done, why not load menus from external text files and skip items via offsets?

The possibilities are endless and I'm sure you can come up with a better way (also download this thing) to write menus! Come on! Be wild! Be creative! Drink detergent! Wait no. Don't drink detergent please. Thanks.