Disabling Default Modifier (CTRL/SHIFT/ALT/DoubleClick) Behavior for Sliders, Buttons, Tables
-
Is this possible?
I have some controls that I'd like to be DAW automate-able, but am reading here that panels can't do that. https://forum.hise.audio/topic/7514/panel-ispluginparameter/
The panel is what I go to for fully custom behavior. Right now my panels have a normalized 0-1 output that I scale or curve accordingly and feed that value into whatever. If daw automation were to be implemented for panels, I imagine it would strictly adhere to the set min-max range, as the panel doesn't have the middlePosition parameter.
So I figure I can use the slider with LAF for some of these controls, but would like consistency with modifier behavior (I have double-click for reset, shift click for finetune, alt and ctrl are for custom stuff on a per-control basis.
-
@aaronventure What about using panels for all controls and having hidden knobs for the automation, those knobs can be linked to the panels.
-
@d-healey How does this work in practice? DAWs have a "show last touched envelope", but the only control the user is interacting with is the panel.
-
@aaronventure There is a link control property in the property editor, if you can link that to a panel I think it will work (I'm not able to check at the moment)
-
@d-healey Passing the panel name in the hidden slider's linkedTo property indeed does link it, but it takes up the panel's min.max as range and overwrites its isPluginParameter and pluginParameterName properties.
Perhaps I can script it, and switch focus to the hidden slider on event.mouseUp.
-
@d-healey Alright, my findings so far:
I was able to link and encapsulate the link by creating the child slider within my encapsulation fuction, storing its reference into data.dawAutomation, then writing another inline function that would get assigned to be the callback on the child slider creation
inline function dawControlCallback(component, value) { //find parent panel by naming scheme local parent = Content.getComponent(component.get("text").replace("autom_", "")); // set its values and repaint parent.setValue(MathX.NormalizeCurve(value, parent.data.curveProperties[0], parent.data.curveProperties[1], parent.data.curveProperties[2])); parent.data.actualValue = value; parent.repaint(); }; inline function createHiddenKnob (curveAmt, min, max, name, fullName, x, y, width, height, defval) { local widget = Content.addKnob("autom_" + name, x-50, y+300); Content.setPropertiesFromJSON("autom_" + name, { "visible": true, "saveInPreset": true, "isPluginParameter": true, "isMetaParameter": true, "pluginParameterName": fullName, "width": width, "height": height/2, "min": min, "max": max, "defaultValue": defval, "stepSize": 0.001, "enableMidiLearn": false, //"parentComponent": name, }); if (curveAmt == 0) widget.set("middlePosition", -1); else widget.set("middlePosition", MathX.CurveAlpha(curveAmt, 0.5, min, max)); Content.getComponent("autom_" + name).setControlCallback(dawControlCallback); return widget;
I did not yet figure out how to trigger the panel's callback without it then looping into the hidden slider callback etc.
Here's my issue: When I move the hidden knob, the panel knob updates properly in real-time. But when I put down some automation in the DAW, the hidden knob updates in real-time as well, but the panel graphic stutters and updates every half a second or so, even though it works fine when I trigger it with the mouse.
The funny thing? If I hover that panel or any of the other panels sharing the same helper function and paint routine, it behaves smoothly as expected.
-
Here's a simple use-case and why I'm proposing this in the first place.
I have a filter frequency knob. Obviously that's not linear, so I need to be able to curve its value to get a desired actual value. The slider can do that, and it properly displays that curved value when automated in a DAW.
But I'm stuck with the default modifier behavior. I want to do my own modifiers. I think modifiers are awesome and extremely underused, they speed up workflows and allow for clutter-free UIs with tons of functionality.
So I go to the panel and script my own behavior. But the panel can't be automated in the DAW! And if it could, its actual value would be what's displayed, and that's no good for the filter frequency usability-wise.
Now it feels like I'm choosing between custom modifier functionality (and mouse behavior) and DAW automation. I think's that's a tough one.
I wish scriptPanel had feature parity with the slider in this regard. That would make it a true omni-widget. We got text/value entry the other day. Amazing. It's still missing:
- middlePosition property and outputting a curved value if middlePosition is defined
- being able to be a plugin parameter and report its curved behavior to the DAW.
-
@d-healey Well, flagging for artificial trigger was simpler than I thought.
parent.data.artificial = 1; parent.changed(); parent.data.artificial = 0;
I then gated off the part of the panel callback where it sets the child/slider value.
And the child calling parent.changed() in its callback fixes the stuttering as well.
Calling data.dawAutomation.changed() on event.mouseUp designates the hidden child/slider as the "last touched" and the DAW properly pulls out its envelope, which displays correctly scaled value range.
Huh.
All fully encapsulated, too. I guess that's it? I'll report back if some issues arise, I haven't actually tested if there are any sound issues with the automation being done this way.
Still feels a bit hacky. My data flow is now DAW automation -> Hidden knob -> Panel -> MIDI script control -> MIDI script code. I guess it's just one extra step.
-
@aaronventure yes, that looks a bit convoluted. What is the other stuff that you want to assign to modifiers? I'm thinking about adding a method to the scripting slider that allows you to change the modifier keys for text-input (shift) and fine control (command / ctrl), this might prevent you from going off the deep end here.
-
@Christoph-Hart Yep, that's me right there.
If the Slider object could take parameters like
- "useDefaultCtrlModifier"
- "useDefaultShiftModifier"
- "useDefaultAltModifier"
that would allow us to disable it, that would be quite simple to use (although I don't know how simple is that implementation upstream).
setKeyPressCallback already exists, so we can technically check for any key combination already. I saw that David implemented isCtrlDown a while ago, I guess Shift and Alt can be implemented the same way, and then these modifiers which are used 99.9% of the time can be checked for using a single line of code.
008a031 - Added Content.isCtrlDown (#314)
Fine control can already be changed by changing the mouseSensitivity property. So in this case it would be a single line in the controlcallback:
slider.isShiftDown() ? slider.set("mouseSensitivity", 0.3) : slider.set("mouseSensitivity", 1.0);
The only thing I don't know (because I skipped on the base controls when I realized I cannot implement custom modifiers) is if it can take doubleClick, but I think that's in the broadcaster (correct me if I'm wrong).
-
@Christoph-Hart Again, my code works right now, it just requires me to call an additional function in the onControlCallback for each control that I want to be DAW-automatable.
I use a helper function to create controls, it takes a simple bool whether it's supposed to be DAW automatable, if yes, it creates a hidden knob and stores a reference into widget.data.dawAutomation by calling another helper function, which defines the hidden knob using parameters that I used to define the actual visible panelKnob and just assigns it the generic callback. That generic callback finds the parent/panelKnob reference by string operations with a pre-set naming scheme, sets the value of the parent and triggers its callback under the Artificial flag, which just tells the function in the panel callback to not trigger the sending of its value to the child because the callback is being called by the child.
it works pretty well in HISE and in compiled plugin, at least visually. I haven't tested in in use because... I'm still working on the UI.
I do believe the playback/DSP setup will be pretty easy as the internals of this fucking spaceship framework are pretty nuts (most of my feedback/requests over the past month have been UI design-related). I'm just pushing the UI/UX part really hard, but I do believe that part of a plugin/product more often than not makes or breaks its success.
Btw. what's up with the mentioned 5 parameter restriction for inline functions? Mine has like 12 and works without issues.
-
@aaronventure said in Disabling Default Modifier (CTRL/SHIFT/ALT/DoubleClick) Behavior for Sliders, Buttons, Tables:
Mine has like 12 and works without issues.
Sounds like an ugly function :p perhaps break it into several smaller functions.
As far as I know that 5 parameter limit is bogus, and you can always get around it by passing in arrays or objects.
-
@d-healey said in Disabling Default Modifier (CTRL/SHIFT/ALT/DoubleClick) Behavior for Sliders, Buttons, Tables:
Sounds like an ugly function :p perhaps break it into several smaller functions.
It already has several smaller functions!
I mean here it is, it's all the things a control could care about.
inline function createKnob (curveAmt, curveMin, curveMax, name, fullName, x, y, width, height, paintRoutine, defval, fontSize, parentComponent, isDawAutomatable)
-
@aaronventure Use an object for the component properties. Added bonus is you can set all the properties in a loop so you don't have to do it one by one.
-
@d-healey Right, but is it a single line of code then?
If so, please teach.
Do you mean declare an object for each control, and then pass that object into the function which accesses pulls out its properties into vars and uses that to do the work?
-
Do you mean declare an object for each control, and then pass that object into the function which accesses pulls out its properties into vars and uses that to do the work?
Yes, I use basically the same UI for all my instruments so I don't have to do this for components, but here's an example where I'm doing it for modules: https://codeberg.org/LibreWave/RhapsodyBoilerplate/src/branch/main/includes/Configuration.js#L138
For components once I've used my factory script to build the UI any modifications I make are in the property editor. For batch assigning look and feel functions I have this monstrosity - https://codeberg.org/LibreWave/RhapsodyBoilerplate/src/branch/main/includes/Card.js#L108
-
@d-healey said in Disabling Default Modifier (CTRL/SHIFT/ALT/DoubleClick) Behavior for Sliders, Buttons, Tables:
Yes, I use basically the same UI for all my instruments
Yeah this is all part of setting up my design language within the HISE environment. I'm also exploring all the new cool shit I can do here and seeing how much eye candy I can stuff in this baby before it starts to cry.
The idea is that once I set it up, I can forget about it, just change the colors as needed and single-line-add any sort of control I want now or in the future.
-
@Christoph-Hart Here's my current situation:
I'm using ScriptSlider. I use the modifiers method you added after this discussion to change the mapping of FineTune to shiftDown.
Great.
But if I want to add functionality using a broadcaster that in any way involves shift, FineTune will always trigger. I cannot tell the modifier method that I want it to do FineTune if it's ONLY shiftDown and ctrl and alt arent' pressed.
This is easy enough with the mouse callback for a panel. In theory it's the same for ScriptSlider with a broadcaster, except mouseSensitivity property of ScriptSlider has a bug where it cannot be set back to 1.0 or anything after it has been set once.
https://github.com/christophhart/HISE/issues/514
The panel is still a no-go because it doesn't support DAW automation as talked about in this thread, even though I'd much prefer doing everything with a panel, especially because its repaintImmediately() method is a lot faster than stock component repaint message.
-
@aaronventure what about panel + hidden slider for automation?
-
@d-healey That will work except with some edge cases, but will also double the count of components in the component list