change param from code: how to disable gui?

Right so you have a module where you don’t have the code, you look it up at runtime, you find its params, you set the param quantity, the UI updates, all fine.

But you also want to deactivate drag messages on that item?

If I understand correctly I think you need to do four things

  1. Write a custom widget which calls e.consume() for all the events. Rack/include/widget/event.hpp at 8c6f41b778b4bf8860b89b36d5503fd37924077f · VCVRack/Rack · GitHub the surge code I linked above shows you the events I chose to capture when deactivate; you could consume those in yours
  2. Create and position that widget over the widget which maps to the param quantity if you can find it. (Finding it isn’t super duper easy but BaconPlugs/src/LintBuddy.cpp at 67cc7d4a7132ed906b4c72ec2ea41340bf2fd13e · baconpaul/BaconPlugs · GitHub code plus some dynamic casting will get you there I bet
  3. Remove that widget once you are done with your change. Make sure it is in the right position in the Z Order
  4. But make sure to do all that widget manipulation from the UI thread not the DSP thread, so you also have a bit of process-starts-it-and-messages-ui-thread-to-block-it queue shenanigans going on

Help?

1 Like

Dan’s advice is good advice for the z order stuff. And transparent widget if placed in the right z order spot may obviate the need for step 1. Maybe. I think an event which is not acted on but which has a blank implementation and is not consumed doesn’t traverse the hierarchy but I don’t remember for sure. That’s what TransparentWidget does

Oh and my last tip is: don’t use transparent widget at first. It is transparent! You won’t find it. Subclass it and implement draw to make it an alpha 0.5 red box in bounds or some such then you will know if you put it in the right spot

Good luck!

1 Like

So why I see the background of the transparent widget back of the Param its weird :thinking:

Maybe addChildBelow or addChildAbove can help? Or how can I place my widget between two widgets?

What I’ve already tried. But placing “over” seems not sufficient…

OK, I’ll try to explain this better… (is English not your first language? If not, you may have a better time on the Discord where you can have a more interactive conversation)

I have not verified this, I may be incorrect about certain points and I’m trying to simplify things.

Each module is a widget, and each thing you add to a module is also a widget (lets ignore drawing directly with Nanovg for now…).

Think of each of those “things” as a node, and all the nodes are related to each other in some way.

This is because all the nodes are part of the total tree (This is similar to how many graphics applications work, or game engines, or even how HTML nodes work).

Lets consider the module node as the tree “root” (or the tree trunk), all the other nodes that we add to the module are further up the tree. So for example, if you do module->addChild(gainKnob) the module is the parent node, and it now has a single child node of gainKnob.

Now lets add a 2nd child: module->addChild(driveKnob)

The tree now looks like so: module → [driveKnob, gainKnob ] The module node is the tree trunk, and the two knobs are tree branches. Note of course that each of the two knobs could have its own children too.

This also shows how the children of nodes have an order, if you want to add a new node inbetween the two knobs, you could for example use module->addChildBelow(toggleSwitch, driveKnob)

Now think about what happens inside Rack when we draw the module: First we draw the module (ie the module panel), then we draw the children on top, otherwise the module panel would hide the knobs.

So now, if you understood that, you should see that in order for the TransparentWidget to block events from reaching a certain node, such as the gainKnob, it needs to be the parent of that node. But in order for a widget to be drawn on top of another node, it has to be further up the tree structure, for example a child of the gainKnob would draw on top of that knob.

So, in summary, to have a TransparentWidget to block the events from a knob, and to draw the grey overlay that you want, you would need a layout something like this:

UnclickableLayer->addChild(myParam->addChild(greyOverlayWidget))

I really hope that helps, if not, I am sorry but I think I am at my limit of educational skill… sorry :sleeping:

Yeah its all clear man, thanks. What I don’t see is a way to place widgetA between widgetB and widgetC (at Z layer level).

Let see that widgetB is at z-index 1, and widgetC at z-index 2. What I need here is to place widgetA at z-index 2, moving widgetC at z-index 3.

So having widgetB (1), widgetA (2) and widgetC (3).

addChildBelow and Above seems to deal with siblings, so I think will place widgetA at same level of widgetC (so both z-index 2), which I don’t think is what I need. Correct?

In few words: consider that param widget is already there, can’t create from scratch the chain, and I just look for a way to insert it at correct z-index :slight_smile:

Well, that depends on if our different words mean that same thing… is a lower z-index drawn on top of or below a higher z-index?

Even if the param widget has already been added to the tree, you can still add children to it, or even add siblings (as children of its parent). You possibly could also remove that param widget from the tree and then re-add it under a different or new parent, although this is probably not appropriate for what you are trying to achieve.

In any case, if you have a reference to the param widget, simply adding a child to it will allow you to draw on top of it, this is how you can add the grey overlay.

To prevent the events is the trickier part since the TransparentWidget that does this needs to be the parent of the param widget.

As I said above, this might be possible, but I think you would be better following Paul(bacon that is)'s way of creating a new widget that captures all the events and then decides which to prevent…

Yes, thats the hard part, which dunno how to achieve :thinking:

Tomorrow I’ll try @baconpaul suggestion anyway, and let you know, thanks. Have a nice night:)

Once you find the Param widget I would place myself as a child in the widgets parent at the same spot

I think Dan is probably right and you want add child below but he may be wrong

If you paint the widget overlay it will be clear though

So

W = find widget first mapped to Param If (w->parent) W->parent->add child my overlay

Also be careful with the lifetime of the overlay. If you remove child it you have to free it

Keep in mind that in headless mode, there are no ModuleWidgets – only Modules. But there are params and even lights (which is a little weird).

1 Like

Yeah in this “kind”’of rack code my modus operandi is to check everything for null even if I’m sure it’s not :slight_smile:

1 Like

Here we go: tried @baconpaul suggestion, extending Widget class, but I can still move the knob:

struct UnclickableLayer : Widget {
	UnclickableLayer(Vec size, Vec pos = {0.0f, 0.0f}) {
		box.size = size;
		box.pos = pos;
	}

	void draw(const DrawArgs &args) override {
		// background
		nvgBeginPath(args.vg);
		nvgRect(args.vg, 0, 0, box.size.x, box.size.y);
		nvgFillColor(args.vg, nvgRGBA(255, 0, 0, 128));
		nvgFill(args.vg);
		nvgClosePath(args.vg);
	};

	virtual void onHover(const HoverEvent &e) override {
		e.consume(this);
	}
	virtual void onButton(const ButtonEvent &e) override {
		e.consume(this);
	}
	virtual void onDoubleClick(const DoubleClickEvent &e) override {
		e.consume(this);
	}
	void onHoverText(const HoverTextEvent &e) override {
		e.consume(this);
	}
	void onHoverScroll(const HoverScrollEvent &e) override {
		e.consume(this);
	}
	void onEnter(const EnterEvent &e) override {
		e.consume(this);
	}
	void onLeave(const LeaveEvent &e) override {
		e.consume(this);
	}
	void onSelect(const SelectEvent &e) override {
		e.consume(this);
	}
	void onDeselect(const DeselectEvent &e) override {
		e.consume(this);
	}
	void onSelectKey(const SelectKeyEvent &e) override {
		e.consume(this);
	}
	void onSelectText(const SelectTextEvent &e) override {
		e.consume(this);
	}
	void onDragStart(const DragStartEvent &e) override {
		e.consume(this);
	}
	void onDragEnd(const DragEndEvent &e) override {
		e.consume(this);
	}
	void onDragMove(const DragMoveEvent &e) override {
		e.consume(this);
	}
	void onDragHover(const DragHoverEvent &e) override {
		e.consume(this);
	}
	void onDragEnter(const DragEnterEvent &e) override {
		e.consume(this);
	}
	void onDragLeave(const DragLeaveEvent &e) override {
		e.consume(this);
	}
	void onDragDrop(const DragDropEvent &e) override {
		e.consume(this);
	}
	void onPathDrop(const PathDropEvent &e) override {
		e.consume(this);
	}
};

using addChild to the paramWidget:

ParamWidget *paramWidget = getParam(16); // 16 = ID of my Knob
paramWidget->addChild(new UnclickableLayer(paramWidget->box.size));

Am I wrong on doing it? Or just “it doesn’t work” as expected? I can cleary see the red layer on the knob, but I can still move it via GUI…

I think this is nearly right, you are adding your new widget as a child of the paramWidget so it draws on top.

paramWidget inherits from opaqueWidget, which says:

So I think the issue is that in your code you have the virtual keyword on the onHover and onButton methods, try removing that

I’m still in morning mode, so brain is a little foggy…

Removed: nothing change. I can still move the knob…

Oh, does the paramWidget consumes the event, and so doesn’t pass it to the child…

If I get time, I’ll take a better look at this at the weekend, but right now I don’t think this is going to work for params that your module does not own, unless you want to try to reparent them in the tree…

edit: if I were trying to make this work, my next step would be debugging, I would want to see what methods are being called when and what events are generated and handled by the different widgets… I would hope that would point me in the right direction

replace paramWidget->addChild with paramWidget->parent->addChild would be my next guess

To replace a widget, you must remove it from it’s parent’s list of children.

That’s true but op doesnt want to replace it rather wants to place another widget atop it in the parent z order.

Right - child widgets are normally drawn in order, from first added to last since addChild appends. This is the default for Rack widgets. Children added later draw on top of prior children. A widget can control whether it paints under or on top of its children by overriding draw, and changing where it calls its base class draw relative to any drawing that it does.

A widget can draw children in any order it wants to by overriding draw and never calling the base class draw.

Draw order is distinct from input handling. Input events are more complicated due to the recursive dispatch of events, setting the target, etc. I wish the Rack docs went into more detail on the design and protocols for input event handling.