OS8 ProffieOS Menu System

Thanks. I’ll try to digest

I also need to document all of these classes better.

I think we need to ignore stabs, melt, clash etc… when in menu mode. Well, specifically ColorChange mode because that’s when SaberBase::IsOn()

Also, this shouldn’t be needed if we can’t turn off because it’s already off, or if there’s no control to turn off in the case of being in ColorChange.

Maybe that needs to stay for other props though?

I’ll override Clash2().

I think Edit Blade Length needs a “select blade” level.
It currently only operates on the first blade in the BladeConfig.

I think we can check that.
If any props need it we should either keep it, or move that line into those props.

Why? Seems like a feature to be able to trigger effects to see what they will look like…

My original thinking was that blade length was really only for blade 1, but I guess if you have multiple replaceable blades, (Like quillions, or a maul) then it makes sense to make it possible to change the length of those blades as well. Maybe we need a define or something which specifies which blades are removable?

Ah. I see, like a live preview.

I think the naming is why I’m confused to a point.
By confusing names I mean we have MODES and MENUS. But it seems “Mode” is used interchangeably.
For example, like in the ColorChangeMode, we have a ColorChangeMenu. That makes sense.
But then we have
typedef mode::SelectCancelMode SelectCancelMode;
and not
typedef mode::SelectCancelMode SelectCancelMenu; ?

I might be crazy but seems this is part of my confusion.

This is my scrambled brain:
A MENU_SPEC_TEMPLATE is used with the struct, like I see

#ifndef COLOR_CHANGE_MENU_SPEC_TEMPLATE
#define COLOR_CHANGE_MENU_SPEC_TEMPLATE ColorChangeOnlyMenuSpec
#endif  

ok and that struct is from this, which seems to be a list of all the things mode needs to work

template<class SPEC>
struct ColorChangeOnlyMenuSpec {
  typedef mode::SelectCancelMode SelectCancelMode;
  typedef mode::SteppedMode<SPEC> SteppedMode;
  typedef mode::SmoothWraparoundMode<SPEC> SmoothWraparoundMode;
  typedef mode::SmoothVariationMode<SPEC> SmoothVariationMode;
  typedef mode::SteppedVariationMode<SPEC> SteppedVariationMode;
  typedef mode::ColorChangeMode<SPEC> ColorChangeMenu;
};

So to go into that mode, and use those list menus, we use this

      pushMode<MKSPEC<COLOR_CHANGE_MENU_SPEC_TEMPLATE>::ColorChangeMenu>();

which uses the ColorChangeMenu of the ColorChangeMode of the ColorChangeOnlyMenuSpec of the COLOR_CHANGE_MENU_SPEC_TEMPLATE
…and then it it works? :upside_down_face:

I’ve been going back and forth with NoSloppy a bit about this, and I just wanted to post this here to bring some of it out of DMs.

It’s a lot of droning on I feel, but it’s basically my dive into the code and me writing my thoughts about it as I go. Being fairly new to ProffieOS, perhaps some things are “duh” things that I should’ve known, but maybe @profezzorn what my experiences were going through things would be valuable as far as what’s confusing to someone on the outside trying to come in.

I can understand if it’s confusing, because I haven’t tried to enforce any kind of naming rule between MODE and MENU. Technically a MODE is anything that inherits from “class ModeInterface”, and you could use MODEs to implement other things than just menus. You could for instance create a “BrokenMode” which just makes scratchy noises when you press buttons, but which goes away after a minute or two, then you could do pushMode<BrokenMode>() when a cortosis clash occurs…

What makes things confusing is that every mode I’ve implemented so far is a menu of some sort, so for right now, they are interchangeable. From a more object-oriented perspective though, only things that inherit from MenuBase should actually be called menus. SelectCancelMode does not and therefor it is a Mode, not a Menu.

However, this might be confusing, because really anything that inherits from SelectCancelMode behaves a lot like a menu.

Yes? Is there a question I can answer here?

Yeah, it’s wandering around a bit. I don’t think there is anything wrong in there, it just might not be the best way to explain things.

All of these things needs to be documented better. Not sure when I’ll get around to that, but hopefully soon.

What makes all of this extra confusing is that the SPEC construct is to the best of my knowledge entirely new. It’s a style/paradigm/trick that has never been used this way before, so it’s not like you can go read about it anywhere.

I’m not sure if a few short paragraphs would be helpful, but here is how I would define some terms, and explain why they exist at all:

Mode

A “Mode” is a class/struct which can take over some input handling from the Prop temporarily. The Mode may do anything it wants with the input, but the primary purpose of Modes is to implement menus. Modes are stored in a linked list, so that when you exit one Mode, whatever Mode was active before becomes active.

SPEC TEMPLATE

To make it possible to customize menus, structs/classes in the mode/* don’t inherit directly from each other. If they did, they would form an unchangeable tree of classes, and if you wanted to make any changes, you would need to build an entirely new tree. The same thing applies for the tree that is formed by following the menu hierarchy; when you select “Settings”, it doesn’t directly go to the SettingsMenu class, instead it looks up which class to use in the SPEC TEMPLATE. The SPEC TEMPLATE is kind of like a phone book which holds all the things needed for a specific menu hierarchy to work. The power of this construct comes from the idea that you can inherit from any SPEC TEMPLATE and change one or more entries without having to touch the rest of them.

SPEC vs. SPEC TEMPLATE

From a more practical aspect, you can’t look things up in a template. ProffieOS uses something called the Curiously Recursive Template Pattern to turn a template into a regular class/struct in which we can look things up. We need to do this because otherwise the lookups would happen immediately, and inheriting and overriding entries in the SPEC TEMPLATE wouldn’t work. ProffieOS has a template called MKSPEC which actually does the work of converting a SPEC TEMPLATE into a SPEC.

So the above terms are generic, MenuBase, SteppedMode, SelectCancel, etc. are all specific implementations that use the concepts above. I should of course document all of those too, but I think it’s more clear if we keep the generic terms separate from the specific implementations, so I’m going to stop here for now.

1 Like

I found this at the bottom of malloc_helper.

I am at a standstill now as the latest updates are causing an error I’ve spent all night trying to figure out to no avail.
error: 'TopMenu' is not a member of 'MKSPEC<MAKE_SOUND_LIBRARY<SoundLibraryTemplate>::SL_SPEC>' 1189 | #define MENU_SPEC_MENU TopMenu

I am trying to check the custom template i put in my prop to use modes, but I’ve no idea why TopMenu issue is happening.
Worked until the “manage sound library version better” V2 sound library updates.

:man_facepalming: :man_facepalming: :man_facepalming: :man_facepalming: :man_facepalming:

I think after all that it was just supposed to be MENU_SPEC_TEMPLATE instead of FINAL_MENU_SPEC_TEMPLATE??

   void EnterMenu() {
    // pushMode<MKSPEC<FINAL_MENU_SPEC_TEMPLATE>::MENU_SPEC_MENU>();
    pushMode<MKSPEC<MENU_SPEC_TEMPLATE>::MENU_SPEC_MENU>();
  }

No, it’s supposed to be FINAL_MENU_SPEC_TEMPLATE.
There is a bunch of magic in sound_library.h that tries to make sure that sound_library_ and MKSPEC<FINAL_MENU_SPEC_TEMPLATE>::SoundLibrary are the same class, but obviously that’s not working properly right now.

I may try to do this some other way though, what I have right now is rather ugly unfortunately.

I don’t understand how you got this error, because MAKE_SOUND_LIBRARY is not used if MENU_SPEC_TEMPLATE is defined. However, if MENU_SPEC_TEMPLATE is not defined, then EnterMenu() doesn’t exist, which means you shouldn’t get this error…

What am I missing?

May I ask what the point of ::SoundLibrary being in the spec is? That’s something I don’t understand.

It’s to let the spec decide what version of the sound library it needs.
The current default spec needs “SoundLibraryV2Template”.
However, the Fett263 prop will probably keep working just fine with the V1 sound library, and thus all the existing voice packs will keep working. Only new or updated voice packs will work with my menus, since they require a V2 voice pack.

In general, I want everything to be backwards compatible. If you have a voice pack you like , you should be able to keep using it, even if you upgrade ProffieOS, as long as you don’t enable new features which might require new sounds and a new voice pack.

1 Like

Well this must just be me, misunderstanding the order of inclusions.

I was hard baking the define into the top of my prop instead of in the user config. Then, using a different define in the config that determines whether to use either the full default “EnterMenu()” menu system, or a custom spec with just a couple of modes that I am trying to get working.
The idea was I needed to do CheckVersion() and sound library stuff in prop base for use with custom spec, but they’re guarded in ifdefs, and even though the hard-baked define in my prop would enable EnterMenu() , that just wouldn’t get called in any Event2 cases if my custom spec was set to be used instead.
After getting back from includiing prop_base, I’d undef MENU_SPEC_TEMPLATE, then define it with BCMenuSpec instead, and then roll from there.

I was under the assumption the order went like:
in ProffieOS.ino,
#define CONFIG_PROP
#include CONFIG_FILE (so now we’re in there)
We get to #ifdef CONFIG_PROP, which is yes, so we
`#include “…props/saber_BC_buttons.h”
at that point, I thought we could still get MENU_SPEC_TEMPLATE defined before we got to the EnterMenu() function when we include prop_base, so i tried it here:

#ifndef PROPS_SABER_BC_BUTTONS_H
#define PROPS_SABER_BC_BUTTONS_H

#define MENU_SPEC_TEMPLATE DefaultMenuSpec

#include "prop_base.h"
#include "../sound/hybrid_font.h"
#include "../sound/sound_library.h"

but apparently that doesn’t work.
If I just put it in the user config file as designed it compiles no problem.
Just me getting too fancy I guess, but I thought it would be fine.
I’ll just keep trial and erroring away over here.
I know, I know.
“Why can’t you just require the config to use a define for MENU_SPEC_TEMPLATE like it’s designed to work like?”
I guess just trying to avoid another define for the user to manage.

Currently you have to include sound_library.h when you make changes MIN_SOUND_LIBRARY_VERSION, and also when you make changes to MENU_SPEC_TEMPLATE., and you have to do it before prop_base.h, since that’s where they are used.

To make it easier, I’m including sound_library.h from prop_base.h. That means that all the defines will be updated automatically, as long as you make sure the defines have their final values before including prop_base.h.