Controlling a knob automatically from another knob

So I’m looking at overriding on hover to link the inverse of another knob when the opposite knob is hovered && switch engaged. Issue at the moment with doing the following:

if (params[LINKMINMAX_PARAM].getValue() < 1.f) {
	params[MAX_PARAM].setValue(-params[MIN_PARAM].getValue());
	params[MIN_PARAM].setValue(-params[MAX_PARAM].getValue());
} /***else*/

This stops both knobs working.

What I would like to do is this: (without needing to use the switch to enable which one is the slave.)
onHover

if (params[LINKMINMAX_PARAM].getValue() < 1.f) {
	params[MAX_PARAM].setValue(-params[MIN_PARAM].getValue());
} //*** else onHover of MAX_PARAM do opposite as one is master other is slave
else params[MIN_PARAM].setValue(-params[MAX_PARAM].getValue());

The else part becoming an else if (… && onHover of max)

What would be the best way to do so.

The switch will link the inverse but when it is not engaged both knobs should work like normal.

Couldn‘t you simply store which knob has been clicked?
If you don‘t want to depend on ui interaction you could also remember the previous value of the knobs and determine which one has changed, I guess.

Would need on mouse down event to it that way I think. onHover I think is more ideal or onHoverDrag possible more so as just mousing over would probably cause an issue.

What would you do to store the click?

Just saw the edit. As one is dragging the other that value would be always changing, no?

Overriding onDragStart (https://github.com/VCVRack/Rack/blob/cc9e0337e0a25de626591295fc16cb66ecf00053/src/app/Knob.cpp#L31) should work. From there I would write to the module which knob it is. onDragEnd could clear this field.

(My second idea isn‘t that good after thinking over it)

Hmm dunno about that way, that seems less simpler than onHover. My thinking being once it detects being hovered it will step into the else part if the users decides not to drag or moves off the param it steps back to the if.

Should work too I think. I‘m not sure if onHover is called once when „entering“ the knob or on any mouse movement, in case it makes a difference.

Whatever is under the mouse I believe. onHoverDrag would be the mouseDown I think.

It is usually called when something is hovered :face_with_raised_eyebrow: :laughing:

Something worth mentioning is the setter being locked is only the param locking up the actual value will change. So if I was to change the locked param and enable the switch so the getter is now the value changed, the other knob will change to the inverse value.
Like so:
getterSetter

So that’s partly why I’m thinking onHover when the link is enabled

Maybe trying emitting a ‘change’ event after changing the params?
Seems to help “animated” widgets do what I expect.

v1

event::Change ec;
onChange(ec);

v0.6

eventChange ec;
onChange(ec);

I’d personally do this in the DSP kernel (Module::process()). What if the knob is controlled by the VCV MIDI-Map module? You can do something like

// member variables:
float lastX;
float lastY;

// process():
if (x != lastX) {
	y = -x;
	lastX = x;
}
else if (y != lastY) {
	x = -x;
	lastY = y;
}

You can do it every N frames using dsp::ClockDivider if it takes too much CPU.

I had not thought of MIDI-Map or stoermelder’s CV Map’s on the param’s. But they do indeed mess with the locked up setter in a bad way (I’ll pass that bridge when I come to it).

Probably even more of a reason to use onHover. When either are not hovered control can be normal.

Hope I have explained correctly that I want to use onHover as I feel this would be the more direct approach. I’m thinking something along the lines of this:

if (params[LINKMINMAX_PARAM].getValue() < 1.f && 
	theMinParam_isHovered) {
	//setter is the min param
	params[MAX_PARAM].setValue(-params[MIN_PARAM].getValue());
} else if (params[LINKMINMAX_PARAM].getValue() < 1.f && 
	       theMaxParam_isHovered) {
	//setter is the max param
	params[MIN_PARAM].setValue(-params[MAX_PARAM].getValue()); 
}

I do appreciate the help here and thanks for the input, but it’s pretty simple to control the knob automatically from another knob and I’m clearly doing so. It is overriding onHover to pass the control I’m stuck on.

I know there is more than one way to skin a cat I want to skin it on hover :smile:

You can do what you want, but IMO overriding ParamWidget::onHover is a horrible solution.

Might have to rethink it and rearrange the GUI a bit. The other switch is supposed to pad both values by a certain amount but .setValue(val - pad) and .setValue(-val + pad) will disable the knobs from turning. Once you call set there is no turning back (pun intended)

There doesn’t seem to be a way to set and have control at the same time without using some sort of event.

@Vortico care to elaborate a little more? Is there a different event that would better suit. As it is a mouse controls everything in rack or more appropriately a single point what would be the best event to use to determine what should set a what should get.

It is a limitation of setValue() only being able to set one thing at a time that is the determining factor in using the event system to set, it is the most logical solution as it is either true or false that the mouse will be under that knob at any given time.

I have tried your solution but it seems that it is doing the same thing as mine you lose control of one knob. Unless I have interpreted it wrong:

volt1 = params[MAX_PARAM].getValue();
volt2 = params[MIN_PARAM].getValue();

// process():
if (volt1 != prevMax) {
	volt2 = -volt1;     //sets display value but not knob, lose control of knob still
	//params[MIN_PARAM].setValue(-volt1);
	prevMax = volt1;
} else if (volt2 != prevMin) {
	prevMax = -prevMax;
	prevMin = volt2;
}

I already elaborated in my above post. Don’t set param values from the GUI. Do it from the DSP kernel.

Have you? You just said it is a horrible solution I’m asking if there is a better one based on that statement. I’m saying using the event system is the only viable solution as setValue can only do one knob at a time, you lose control of the other knob.

That’s why I added lastX to my code. Don’t call Param::setValue() on a knob unless the other knob has been changed.

Forgive my single mindedness… If one has been changed by the other how is it not changed? What determines the users has not interacted with that knob?

lastX is presumably set at the end of process(). If the knob is changed, then x != lastX in the next process() call.

@Vortico Based on not finding an easy solution would it be worth having a method that can find which param is being hovered in the API?

ideally

if (param[this_param].isHovered() && param[link_param].getValue() > 0.f){
    param[that_param].setValue(-param[this_param].getValue)
} else if (...) {...}

Use case would be thread title. Other use cases could be linking mixer channels to change stereo paired channels when either is changed or Mutes/Solo’s/AUX/Pan. Syncing settings on a stereo delay. Tuning a fixed filter or multi band EQ from any param. Recalling snapshots while still having the ability to edit parameters and or save.

Could be a very useful feature.

I‘m not sure if you really need this? What‘s wrong with vortico‘s suggestion of tracking which value has changed?
Thinking one step further, when using Rack on a touch display „isHovered“ won‘t be helpful.