Best way to code a modal choose dialog

Hey

I need to make a modal choose dialog like the following one:
image

So basically you have a set of options, title and a return for the selected option. What would be a nice way to make this in a generic way?

I just need a little hint to kick-start the code

1 Like

You could have a frame loop inside a function that handles the up/down and re-rending the screen and then the function itself could return the value chosen, although that will bloat your code vs just using a state engine like seen in almost all games. You make the menu a state and enter that state and in your main loop run the menu processing code each time until a choice is made, then switch states appropriately or handle the selection, etc.

Tada:

This might take a bit of getting your head around (mainly because of the progmem reading involved),
but adding new options is as simple as sticking new items in the array.

You might not want to go for a function-pointer system, you might just want to use an ‘item id’ or something (particularly if this is just a ‘select option, get item’ system) but function pointers will facilitate doing more advanced things like healing the player or changing other player stats.

You’ll need to alter the drawing code to make it more like your example, and I haven’t added a demo of adding A-button functionality (though I will if asked nicely) but the concept is there at least. You can sort of see what the idea is.


It can also be simplified by using memcpy_P, which I’ll update into the repo to do when I’ve got a minute.


I have now updated the repo to use memcpy_P, which works out to be slightly cheaper.

3 Likes

Wonderful! It makes complete sense how you designed it :smiley:

3 Likes

When I saw the question it was kind of spooky because I designed/implemented that system just yesterday.

Though my use case is slightly different, I’m using it for a credits page.
Instead of a Menu and MenuOption I’ve got an array of Category with a category title and a list of people’s names.

If you decide to use it, let me know what your experience is like. (E.g. are there any downsides/issues/bugs or if you find other cool ways of using it.)

Hey @Pharap what is the point of using the struct here?:

struct Player
{
// fill with your player
};

using MenuAction = void (*)(Player *);

Also, I see obscure stuff in utilities.h :stuck_out_tongue:

1 Like

Player is just a placeholder for whatever you want to pass to the action associated with each button.

Unless you meant
using MenuAction = void (*)(Player *);
in which case that’s C++'s type-alias using (like typedef, but backwards (or rather the right way around - typdef is the backwards one)) of a function pointer. (You could also use a class with a virtual function if you prefered, but a function pointer is probably cheaper and easier.)


I just updated the repo.

Now MenuAction is using MenuAction = void (*)(Player &); instead, you you don’t have to worry about pointers.

I’ve also added ApplyMenuAction and ApplyProgmemMenuAction and updated the .ino to demonstrate their use.

Basically you just fill in the Player with whatever data the player has and fill in the bodies of the functions GivePlayerTable, GivePlayerOrnament and GivePlayerLaptop.

I’ve only used function pointers to show that the system can give flexibility, depending on your menu system it might be cheaper to use something else (like just an item).

I’m sure you’ll be able to work it out from my changes, but if you have any other questions don’t hesitate to ask. If you already know how your player, inventory system and items are going to be structured I can help you integrate the menu system if you’d like.


They’re not that obscure, they’re just less well known techniques.


Explanation for AsFlashStringHelper:

Arduino provides a type called __FlashStringHelper to allow print systems to differentiate a pointer to a block of text from a pointer to a byte-sized integer (since technically they use the same types).

If you try to use:

// somewhere in global
const char text[] PROGMEM = "Hello World";

// somewhere in a function
arduboy.println(text);

The string gets garbled because it’s interpreted as something different.

However if you do:

// somewhere in global
const char text[] PROGMEM = "Hello World";

// somewhere in a function
arduboy.println(AsFlashStringHelper(text));

You get the correct result, because the text is converted to a const __FlashStringHelper * which the arduboy then correctly interprets as a string in progmem. The reason the F macro doesn’t produce garbled strings is because it returns a const __FlashStringHelper *.


ArrayLength is a function template.
Templates are a very powerful C++ feature that aparently not many people here are particularly well acquainted with.

I can’t really give a proper explanation without explaining templates in full, so I’ll give a very brief glossed-over explanation to give you an idea of how it works.

template< typename T, size_t N > constexpr size_t ArrayLength(T (&)[N])
{
	return N;
}

The template<typename T, size_t N> part is the actual template part, and the T and N are called template arguments.
The T is a name that stands in for any type and the N is a name that stands in for any size_t value. In a sense they’re a bit like actual function arguments in that they can be different values, but they’re constant, exist only at compile time and can refer to type names.

The clever bit is (T (&)[N]). C++ has a thing called ‘template deduction’ where the compiler will try to match up any of the values in the angle brackets based on the arguments given to the function. The argument (T (&)[N]) matches a reference to an array of any type and of any length, the T template argument matches the type and the N argument matches the length. The function itself returns the length and the constexpr makes almost certain that it’s calculated at compile time, so it’s no different to just manually sticking the array length in, except the compiler does it for you.

2 Likes

Thanks a lot for the detailed explanation! I am going to use this in my game :grimacing:

Sometimes I wish to have a better IDE, it is really a mess in arduino to have the .h and .ino files, how they behave is quite weird sometimes

1 Like

No problem, I’m always willing to answer questions, explain things and generally help where I can.

Glad to hear that. Let me know if you have any trouble adapting it.

I tend to use notepad++ or visual studio and set the IDE to ‘use external source’. I have to switch window to compile, but it’s worth the better auto-complete and syntax highlighting.

I use Atom … its quite nice as well.

1 Like

Looks good… and gave me an idea (to prevent this thread going off on an interesting tangent):

I have just had a chance to look at this … this is pretty cool and I love the fact that it uses progmem nearly exclusively. If I was to build an Arduventure II, I would use this!

1 Like

Just a dumb question, how do I compare menus? for example if(EquipmentMenu == Something)

This menu thing would be great thing for an Arduino library, not just for arduboy but as a general stuff

It depends what your code is doing.
If you’ve got a pointer to the menu, e.g. Menu * currentMenu = &EquipmentMenu; then you can just do pointer comparison if(currentMenu == &EquipmentMenu).

Otherwise it’ll need a custom comparison operator to do memberwise comparison, which could get a bit tricky depending on which argument is in progmem and which isn’t.

For the case where neither are in progmem:

bool operator==(const Menu & left, const Menu & right)
{
    return (left.Title == right.Title) // Pointer comparison
       && (left.Options == right.Options) // Pointer comparison
       && (left.OptionsLength == right.OptionsLength);
}

Then it’d be a case of reading them out of progmem beforehand.

Otherwise, if both are in progmem:

bool progmemCompare(const Menu & left, const Menu & right)
{
    return (pgm_read_word(&left.Title) == pgm_read_word(&right.Title)) // Pointer comparison
       && (pgm_read_word(&left.Options) == pgm_read_word(&right.Options)) // Pointer comparison
       && (pgm_read_byte(&left.OptionsLength) == pgm_read_byte(&right.OptionsLength));
}

Please note that I haven’t actually tested either of these because this isn’t something I’ve had to do myself yet, but I’m 95% sure the first one works and 70% sure the second one works.

Thanks! I will test this afternoon. This is only because I want to call the “modal” menu when the current menu is not “empty” (so I would love to compare the current menu to an empty one)

1 Like

If you’re looking for an ‘empty menu’ and you’re using pointers, you could always designate nullptr/NULL as the ‘empty menu pointer’.

Otherwise if you’re passing around progmem pointers all the time then a pointer comparison will be the easiest because there’s no progmem reading involved and the pointers are guaranteed.

If you’re loading the menus out of progmem and into objects, that’s when the comparison starts getting tricky (and to be honest, that’s probably worth delaying until the drawing and selection parts anyway).

If your strings are in an array then you’d just keep track of the menu’s chosen index and you could either check it against the raw pointers or have some defines:

#define EQUIPMENT 1
if (choice == EQUIPMENT) { ... }

Or do the same thing using an enum, etc.