How to modify the context menu of a foreign parameter

Stoermelder mapping modules (uMap for example), and MindMeld PatchMaster update the context menu of mapped parameters, giving them an “Unmap” option. These mapped parameters are in foreign plugins that know nothing about MindMeld or Stoermelder.

I am interested in doing something similar. I have looked at the source code for both, but cannot figure out how (or where) this is done. Anyone have any pointers to get me started?

@marc_boule @stoermelder :grinning:

From a couple of minutes of browsing Stoermelder PackOne, looks like it’s done here:

plugins\vcvrack-packone\src\MapModuleBase.hpp, line 295 in createContextMenu()

(Ed. Could be mistaken, that’s just what I found casually)

1 Like

Just to make sure I understand, in this example below, I have mapped a PatchMaster to a CVMIX knob, and the menu of that knob does now have the option to unmap. So the question is how we were able to get this?

If that is the case, then I believe the answer is that it is done by Rack, and I’m pretty sure nothing in PatchMaster’s code can go in there and change its menu. When the mapping is done using the Rack mechanisms for this, the menu entry gets added automatically in the remote module.

1 Like

Thanks, but that is the parameter context menu for the mapping module (like CV-Map or uMap). I am looking for the parameter context menu for the foreign module that is mapped.

:exploding_head: Oh! I didn’t expect VCV to have a built-in mechanism for this. What is the name of the API method or structure or…? that establishes the mapping? I think I need something different, but I am curious.

I finally figured out how to search the github source code for a string literal, and I found the code where most of the mapped parameter context menu is extended. But it does not include the “Unmap” option, so I guess that is consistent with unmap being part of a built-in VCV mechanism.

Stoermelder defines a ParamWidgetContextExtender struct within src/ui/ParamWidgetContextExtender.hpp. It includes a virtual extendParamWidgetContextMenu(ParamWidget* pw, Menu* menu) method that is overridden by modules like src/cvMap.cpp and src/cvMapMicro.cpp. I am pretty sure I need to study that code for what I want to do.

@marc_boule, can you explain what the Rack mapping mechanism is? I don’t think I’ve come across it. What Rack header is it defined in?

I’ve guessed that it’s about reparenting or making overlay widgets, but if there’s a Rack API for it, great.

The “Unmap” context menu option is built into Rack itself, as part of the parameter mapping mechanism.
VCVRack/Rack/blob/8c6f41b778b4bf8860b89b36d5503fd37924077f/src/app/ParamWidget.cpp#L287

But, there is no stable API for extending Rack’s context menu of any parameter. The trick I am doing is scanning the scene-container for a MenuOverlay-widget. Plus, the last “touched” widget must be a ParamWidget and the last button been pressed must be a right mouse click. If these three conditions are satisfied, a context menu of a parameter must be active.
The file ParamWidgetContextExtender.hpp encapsultes this, so all you need is to inherit ParamWidgetContextExtender on your ModuleWidget and override extendParamWidgetContextMenu as you like.
vcvrack-packone/src/ui/ParamWidgetContextExtender.hpp

Feel free to copy the file to your plugin (you know, if open source your license must be compatible with GPL-3.0-or-later).

2 Likes

It’s been a while since I’ve looked into that stuff, but the best is to look at Andrew’s code in his MidiMap module:

1 Like

That will get pretty funky if the remote parameter uses submenus right? But otherwise what a clever hack!

I also read this code last night looking at this thread and seems the paramhandle weak pointer is pretty important, but I didn’t dig beyond that

Yes, agreed, I think these lines are where all the magic happens:

L53: APP->engine->addParamHandle(...)

L64: APP->engine->removeParamHandle(...)

L164,L227: APP->engine->updateParamHandle(...)

L174,L277 : APP->engine->updateParamHandle_NoLock(...)
2 Likes

Thanks a bunch @marc_boule and @stoermelder! Your answers are exactly what I need to explore some ideas.

Especially Ben - I found your relevant code shortly before your post, but I was struggling with some details due to misconceptions on my part. Your “simple” answer forced me to confront and correct (at least some of) my misconceptions, and now I have a better understanding of how some of the VCV innards work.

2 Likes

SUCCESS!!

Using the information shared in this thread, I was able to create a new Venom Widget Menu Extender module that lets users provide custom names for parameters and ports, and custom defaults for parameters.

Rather than import Ben’s ParamWidgetContextExtender.hpp, I incorporated the techniques directly into my module widget step() routine, and adapted it to work with ports as well as parameters.

Anyone interested can look at the WidgetMenuExtender.cpp source code starting at (or around) line 406.

Thanks again Ben @stoermelder, without your trailblazing work I could never have realized this module.

6 Likes

The pre-existing context menu can be arbitrarily complex, and the hack still works. The hack simply tacks on additional entries onto the root menu that already exists.

I even tested my module along with CV-MAP, with both manipulating the same parameter. Everything worked flawlessly - both the Widget Menu Extender and CV-MAP extensions were appended to the context menu. My parameter custom name automatically gets propagated to the CV-MAP, which is pretty cool.

The only possibly confusing issue is the order of the context menu extensions can vary, I think based on the order the modules were added. But that is to be expected.

Exactly right.

Cool - what a neat idea

I hear that some devs (me) also do some stuff like remove items from the Param widget menu in some cases …. If you get a bug report in airwindows let me know :wink:

Also surge is pretty dynamic with parameter info in some cases. If you get bug reports about that also let me know!

Yes - Air Windows changing effects was a major part of my testing. The plugin is designed to handle addition/deletion of parameters or ports. I never blindly assume the object still exists after I have made a change, and I gracefully ignore invalidated changes. I also ignore invalidated changes when I write the json.

I also handle the situation when a module renames a port or parameter after I have made a change - module overwrites supersede my changes. If my cataloged name change does not match the current name, then I drop my name and use the module’s name.

With something like Air Windows, the names remain constant as long as you don’t change the effect. So my widget rename works. If you change the effect, then Air Windows changes the name and my name is dropped.

But with PatchMaster, it continuously updates the name of the controls, so you can never effectively rename a PatchMaster control with my module.

I am thinking the only way my module could cause a problem would be if the module with the renamed parameter/port has code that relies on the display name. But I view the name as for display only. I would be shocked if someone has code depending on the name. First off, the vast majority of modules simply configure the name and forget it. For the few that do manipulate the name, I should think they would key of the parameter ID, not the name.

I considered an option to rename modules. But that information is in the Model, and each model stores a reference to the model. I assume multiple copies of a module point to the same instance of the model, so a name change would be global, and would not be good.

1 Like

In my modules I use model->slug in logic to figure out what my modules are connected to. I recommend not changing a foreign module slug, if that is even possible.

Most definitely! Changing the slug would be a terrible idea!

I have chosen to stay clear of module (or model) level changes. My module only makes changes at the parameter and port level, changing only the name. And for parameters it can also change the default value.

1 Like

You saw my other post though that toggling hidden works if you want to have a super favorites mode