.claude/skills/igraphics-ui/SKILL.md
This skill should be used when the user asks to "create a UI", "add controls", "layout controls", "design the interface", "add a knob", "add a slider", "add a button", "add a meter", "add a spectrum analyzer", "style controls", "theme the UI", "use IVStyle", "use ISender", "create a custom control", "add a keyboard", "make a resizable UI", or discusses IGraphics layout, control selection, styling, or visualization in an iPlug2 plugin.
npx skillsauth add iplug2/iplug2 igraphics-uiInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Guidance for building IGraphics-based UIs in iPlug2 plugins. Covers layout strategies, control selection, styling, audio-to-UI visualization, and custom controls.
Every IGraphics plugin constructs its UI with two lambdas in the plugin constructor, inside an #if IPLUG_EDITOR guard:
mMakeGraphicsFunc = [&]() {
return MakeGraphics(*this, PLUG_WIDTH, PLUG_HEIGHT, PLUG_FPS,
GetScaleForScreen(PLUG_WIDTH, PLUG_HEIGHT));
};
mLayoutFunc = [&](IGraphics* pGraphics) {
pGraphics->AttachCornerResizer(EUIResizerMode::Scale, false);
pGraphics->AttachPanelBackground(COLOR_GRAY);
pGraphics->LoadFont("Roboto-Regular", ROBOTO_FN);
pGraphics->EnableMouseOver(true);
pGraphics->AttachTextEntryControl();
pGraphics->AttachPopupMenuControl(DEFAULT_LABEL_TEXT);
pGraphics->AttachBubbleControl();
const IRECT b = pGraphics->GetBounds();
// Attach controls here...
};
For responsive/resizable UIs, add pGraphics->SetLayoutOnResize(true) and use the NControls() early-return pattern to reposition controls on resize without recreating them:
if (pGraphics->NControls()) {
// Reposition existing controls
pGraphics->GetControl(idx)->SetTargetAndDrawRECTs(newBounds);
return;
}
Reference examples: Examples/IPlugEffect/ (minimal), Examples/IPlugInstrument/ (intermediate), Examples/IPlugControls/ (comprehensive).
Select the simplest strategy that meets the need:
| Scenario | Strategy | Key Methods |
|----------|----------|-------------|
| 1-3 controls | Manual IRECT | GetBounds().GetPadded(-10), GetCentredInside(100) |
| Regular grid | GetGridCell | b.GetGridCell(cellIdx, nRows, nCols) with nextCell() lambda |
| Proportional panels | Fraction/strip | FracRectVertical(), ReduceFromLeft(), SubRectVertical() |
| Resizable window | Responsive | SetLayoutOnResize(true) + GetBounds lambda |
| Complex flex | IFlexBox | Yoga wrapper: Init(), AddItem(), CalcLayout(), GetItemBounds() |
The grid pattern with nextCell()/sameCell() lambdas (from IPlugControls) is the most versatile for medium-complexity UIs:
int cellIdx = -1;
auto nextCell = [&]() {
return b.GetPadded(-5.).GetGridCell(++cellIdx, nRows, nCols).GetPadded(-5.);
};
auto sameCell = [&]() {
return b.GetPadded(-5.).GetGridCell(cellIdx, nRows, nCols).GetPadded(-5.);
};
For proportional panel layouts, ReduceFrom* methods are powerful because they mutate the source rect and return the removed portion:
IRECT b = pGraphics->GetBounds().GetPadded(-5);
const IRECT topBar = b.ReduceFromTop(50.f); // topBar = 50px strip, b shrinks
const IRECT sidebar = b.ReduceFromLeft(100.f); // sidebar = 100px strip, b shrinks
// b now contains the remaining area
For detailed IRECT method reference and layout examples, consult references/layout-patterns.md.
Prefer IV (vector) controls -- no bitmap assets needed, styleable via IVStyle, backend-agnostic. Use IB/ISVG controls only when artist-provided assets dictate a specific look.
| Need | Control Type |
|------|-------------|
| Standard UI widgets | IV controls (IVKnobControl, IVSliderControl, etc.) |
| Artist-designed bitmap look | IB controls + LoadBitmap(RESOURCE_FN, nFrames) |
| Scalable artist assets | ISVG controls + LoadSVG(RESOURCE_FN) |
| Quick inline custom drawing | ILambdaControl with draw lambda |
| Complex custom interaction | Custom IControl subclass |
Attach controls with: pGraphics->AttachControl(new IVKnobControl(bounds, paramIdx, "Label", style), ctrlTag, "groupName")
paramIdx: links to a plugin parameter. Use kNoParameter for unlinked controls.ctrlTag: integer tag for lookup via GetControlWithTag(tag). Required for ISender targets.groupName: string for batch operations via ForControlInGroup("name", func).For the complete catalog of available controls, consult references/controls-catalog.md.
Create a shared style and reuse it across controls for visual consistency:
const IVStyle style = DEFAULT_STYLE
.WithColor(kBG, COLOR_BLACK)
.WithColor(kFG, IColor(255, 128, 128, 128))
.WithRoundness(0.1f)
.WithFrameThickness(3.f)
.WithDrawShadows(false)
.WithLabelText(IText(12.f, EAlign::Center));
The 9 EVColor slots control different aspects: kBG (background), kFG (foreground/off), kPR (pressed/on), kFR (frame), kHL (highlight/hover), kSH (shadow), kX1 (indicator track), kX2, kX3 (extras).
For the full IVStyle API, IVColorSpec, IText, IColor, and gradient patterns, consult references/styling-and-theming.md.
To display real-time audio data (meters, scopes, spectrums), use the ISender pattern:
| Sender | Control | Use Case |
|--------|---------|----------|
| IPeakSender<N> | IVMeterControl<N> | Peak meters |
| IPeakAvgSender<N> | IVLEDMeterControl<N> | LED meters with ballistics |
| IBufferSender<N> | IVScopeControl<N> | Oscilloscope |
| ISpectrumSender<N> | IVSpectrumAnalyzerControl<N> | FFT spectrum |
| ISender<N,Q,T> | IVDisplayControl or custom | Generic data |
Setup steps:
.h: IPeakSender<2> mMeterSender;enum ECtrlTags { kCtrlTagMeter = 0 };pGraphics->AttachControl(new IVMeterControl<2>(bounds), kCtrlTagMeter);ProcessBlock: mMeterSender.ProcessBlock(outputs, nFrames, kCtrlTagMeter);OnIdle: mMeterSender.TransmitData(*this);OnReset: mMeterSender.Reset(GetSampleRate()); (for senders that need it)For architecture details and spectrum analyzer bidirectional messaging, consult references/visualizers-and-isender.md.
pGraphics->GetControlWithTag(kCtrlTag) for direct access.AttachControl, then ForControlInGroup("name", func) or HideControl(paramIdx, bool).control->SetActionFunction([](IControl* pCaller) { ... }) for click callbacks.SetAnimationEndActionFunction(func) runs after splash/animation completes.SendControlMsgFromDelegate(ctrlTag, msgTag, dataSize, pData) for plugin-to-control messages.Decision tree:
ILambdaControlIControlIControl + mix in IVectorBaseIContainerBaseFor custom control patterns, layer caching, drawing primitives, Skia vs NanoVG, and animation, consult references/custom-controls.md.
| Concept | File |
|---------|------|
| IRECT, IColor, IText, IVStyle | IGraphics/IGraphicsStructs.h |
| IControl, IVectorBase, IBitmapBase | IGraphics/IControl.h |
| All built-in controls | IGraphics/Controls/IControls.h |
| ISender, IPeakSender, ISpectrumSender | IPlug/ISender.h |
| Yoga flexbox | IGraphics/Extras/IGraphicsFlexBox.h |
| VS Code snippets | .vscode/IGraphics.code-snippets |
| Minimal example | Examples/IPlugEffect/ |
| Intermediate example | Examples/IPlugInstrument/ |
| Comprehensive showcase | Examples/IPlugControls/ |
| Visualizer example | Examples/IPlugVisualizer/ |
tools
This skill should be used when the user asks to "create a web UI", "add a WebView", "build an HTML interface", "use Svelte", "use p5.js", "use three.js", "use React", "use web components", "send messages to JavaScript", "receive messages from JavaScript", "hot reload the UI", "use IWebViewControl", "embed a WebView", "WebView editor delegate", "use IPlugSendMsg", "web-based plugin UI", "load HTML in plugin", "use Vite", or discusses WebView setup, JS/C++ messaging, or web framework integration in an iPlug2 plugin.
tools
Validate iPlug2 plugin builds using format-specific validators (auval, pluginval, vstvalidator, clap-validator) (project)
tools
Download iPlug2 dependencies including plugin format SDKs (VST3, CLAP, WAM) and optional Skia graphics backend libraries
tools
Take a screenshot of the plugin UI using the standalone app CLI for debugging and documentation