I’ve been working on abstracting menu systems for a while.
While I’m still not done, I’ve done enough to start checking things in, so I thought I’d write something to explain a little bit about how it works.
At the most fundamental level, a “mode” can be basically anything. When a mode is active, most or all events gets forwarded to the mode instead of getting handled by the prop. There is a linked list of modes, which works like a stack, so you can “push” a mode onto the stack, and when you pop it, it goes back to whatever mode it was on before. The prop itself is the default mode.
From there on, I’ve tried to decompose mode behaviors into class templates that can be easily changed so that different menu systems can be created without having to re-write everything from scratch. This decomposition is done with C++ templates, and in some ways it’s similar to how the style system works, but in others it’s not, so let’s take a look at how it works.
First of all, there is a class hierarcy:
* ModeInterface : base class for all modes
* SelectCancelMode -> ModeInterface: base class for modes which have Select / Cancel functionality.
* SmoothMode -> SelectCancelMode: Base class for smooth rotation, like color wheels
* SteppedMode -> SelectCancelMode: Base class for any type of menu that has discrete steps
* MenuMode -> SteppedMode: Base class for any menu that has a known number of steps
There are going to be a lot more mode classes, but these should serve as an example of how everything is split up into pieces. Normally when you write C++, you just write the class hierarchy directly. This is easy and fairly easy to extend, but it it becomes ugly if you want to make changes to one of the base classes. Let’s say for instance that you wanted to change the buttons for the “SelectCancelMode”. You can easily write your own mode that works exactly like SelectCancelMode, but since all of the other modes listed above inherit from SelectCancelMode, your class won’t do anything unless you either modify those clases to inherit from your new class, or, you make your own versions of all the classes.
To fix this, I’ve created something called a “Menu Specification Template”. This is a templated class which in a simple case can look like this:
template<class SPEC>
struct ColorChangeOnlyMenuSpec {
typedef mode::SelectCancelMode SelectCancelMode;
typedef mode::SteppedMode<SPEC> SteppedMode;
typedef mode::SmoothMode<SPEC> SmoothMode;
typedef mode::SmoothVariationMode<SPEC> SmoothVariationMode;
typedef mode::SteppedVariationMode<SPEC> SteppedVariationMode;
typedef mode::ColorChangeMode<SPEC> RootMenu;
};
Now, as you can see almost all the modes listed above are actually templates which takes the SPEC as an argument. And when SteppedMode wants to inherit SelectCancelMode, it doesn’t inherit it directly, instead it inherits it from SPEC::SelectCancelMode. This means that if you want to replace SelectCancelMode, all you would need to do is to create a SPEC which overrides it, like:
template<class SPEC>
struct MyColorChangeSPEC : public ColorChangeOnlyMenuSpec<SPEC> {
typedef MyAwesomeSelectCancelMode SelectCancelMode;
};
Then when any class in the menu system needs a “SelectCancelMode”, it will use your new cancel mode (which is apparently awesome).
The goal here is to make it easy both to add/remove menu entries, and to change how the menu system behaves at a fundamental level.
Anyways, some of this code is already in github, and there is more coming, so stay tuned.