Search guides and the APICtrl K
  1. API reference
  2. Native GUI

gfx/plugin_editor.h

Binding the native UI to plugin parameters, and the editor a plugin gets when it says nothing about its own.

#include <sonore/gfx/plugin_editor.h>
On this page

Design notes

Why the widgets did not do this themselves

A Slider knows what a drag means and nothing about audio. This file is where the two meet: the same reason a parameter attachment is kept separate from the slider it drives.

The split is what makes both testable: the widget's rules can be checked with no plugin, and the binding's rules -- which curve, which gesture, which direction the value flows -- can be checked with no window.

The loop this has to avoid

A host changes a parameter, the editor updates its knob, the knob reports a change, the plugin sets the parameter, the host notices... In a DAW with automation running that loop closes thirty times a second and the parameter never settles. Everything arriving FROM the host uses setValue(notify=false), and that is not an optimisation.

Types 6

What an editor needs from whatever is hosting it.

Function objects rather than an interface to implement, so the CLAP, VST3, AU and standalone paths each supply four lambdas rather than each growing a subclass -- and so a test can supply four lambdas of its own.

Binds one Slider to one parameter.

Conversion goes through toNormalisedValue/toPlainValue -- the SAME curve the host's automation lane uses. A knob with its own curve would sit at a different place from the lane drawing the same value, and a user would be right to call that a bug.

The same, for a two-state parameter drawn as a toggle.

A parameter with exactly two positions is on or off, and a drop-down with two entries is the wrong control for it: two clicks and a moment's reading to do what one click should, and nothing legible at a glance across a row of them. Bypass, invert, sync, mute -- these are switches.

The button carries the value NAME rather than a fixed label where the table gives one, so a parameter whose states are "Pre" and "Post" says which it is rather than glowing or not glowing and leaving the user to guess which way round it goes.

The same, for a stepped parameter drawn as a list of named choices.

class GenericEditor : public Widget

The editor a plugin gets when it supplies none of its own.

A generic processor editor, and it exists for the usual reason: a plugin must never be faceless. Until now a webview that could not start left the user one line of text and no controls at all -- the plugin played, automated and saved, and nothing in its own window could be touched.

It is deliberately a plain list. A generated plugin that wants a face builds one; this is the floor, and a floor that tried to look designed would be a second design to keep in step with the first.

-- Groups -----------------------------------------------------------------

A plain list stops being a floor somewhere around thirty parameters, where it becomes a wall the user has to read all of to find one control. Every wrapper already reports ParamInfo::group -- CLAP module path, VST3 unit, AU clump, LV2 port group -- so a HOST's own generic panel has been showing these parameters grouped while ours showed them flat. The data was there; only this used none of it.

The layout follows what the hosts do with a mixed table: parameters with no group stay at the top as ordinary rows, and each declared group becomes a section under them. A table with no groups at all therefore lays out exactly as it did before -- no panel is built, and the flat list is not a special case of anything.

Sections start OPEN, all of them. A closed section is invisible to a screen reader as well as to the eye, so opening collapsed would have taken parameters that used to be reachable and hidden them behind a control the reader cannot see -- the panel paints its headers rather than making them components. Open-by-default keeps every parameter reachable on the first frame and leaves collapsing to the user, which is the direction that cannot lose anything.

class AudioProcessorEditor : public Widget

A plugin's OWN native editor -- the reference's AudioProcessorEditor.

GenericEditor is what a plugin gets when it says nothing. A plugin that wants a face of its own derives from this, builds it from the same widgets, and hands it over from its DSP:

std::unique_ptr<gfx::AudioProcessorEditor> createEditor(const gfx::EditorHost& host);

[main-thread], called each time a host opens the editor. NativeEditor puts the result in its window instead of the generic one -- with the same clock, tooltips, resize border and drop-down menus -- and destroys it when the host closes the window. The DSP outlives every editor it makes, so the editor may keep a reference to it, as the reference's editor keeps one to its processor; anything it hands the audio thread still crosses by atomics.

It is a Widget and nothing more, so it renders into a Bitmap with no window at all: createEditor() with four lambdas for a host, paintTree() into a Graphics, and the pixels are there to check.

↑↓ to move↵ to openEvery public name in the headers, and every guide