AgentSkillsCN

igraphics-ui

当用户要求“创建 UI”、“添加控件”、“布局控件”、“设计界面”、“添加旋钮”、“添加滑块”、“添加按钮”、“添加仪表”、“添加频谱分析仪”、“美化控件”、“为主题化 UI”、“使用 IVStyle”、“使用 ISender”、“创建自定义控件”、“添加键盘”、“制作可调整大小的 UI”,或讨论 iPlug2 插件中的 IGraphics 布局、控件选择、样式设计或可视化时,应使用此技能。

SKILL.md
--- frontmatter
name: igraphics-ui
description: 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.

IGraphics UI Authoring

Guidance for building IGraphics-based UIs in iPlug2 plugins. Covers layout strategies, control selection, styling, audio-to-UI visualization, and custom controls.

Core Pattern

Every IGraphics plugin constructs its UI with two lambdas in the plugin constructor, inside an #if IPLUG_EDITOR guard:

cpp
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:

cpp
if (pGraphics->NControls()) {
  // Reposition existing controls
  pGraphics->GetControl(idx)->SetTargetAndDrawRECTs(newBounds);
  return;
}

Reference examples: Examples/IPlugEffect/ (minimal), Examples/IPlugInstrument/ (intermediate), Examples/IPlugControls/ (comprehensive).

Choosing a Layout Strategy

Select the simplest strategy that meets the need:

ScenarioStrategyKey Methods
1-3 controlsManual IRECTGetBounds().GetPadded(-10), GetCentredInside(100)
Regular gridGetGridCellb.GetGridCell(cellIdx, nRows, nCols) with nextCell() lambda
Proportional panelsFraction/stripFracRectVertical(), ReduceFromLeft(), SubRectVertical()
Resizable windowResponsiveSetLayoutOnResize(true) + GetBounds lambda
Complex flexIFlexBoxYoga wrapper: Init(), AddItem(), CalcLayout(), GetItemBounds()

The grid pattern with nextCell()/sameCell() lambdas (from IPlugControls) is the most versatile for medium-complexity UIs:

cpp
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:

cpp
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.

Choosing Controls

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.

NeedControl Type
Standard UI widgetsIV controls (IVKnobControl, IVSliderControl, etc.)
Artist-designed bitmap lookIB controls + LoadBitmap(RESOURCE_FN, nFrames)
Scalable artist assetsISVG controls + LoadSVG(RESOURCE_FN)
Quick inline custom drawingILambdaControl with draw lambda
Complex custom interactionCustom 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.

Styling with IVStyle

Create a shared style and reuse it across controls for visual consistency:

cpp
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.

Audio-to-UI Data (ISender)

To display real-time audio data (meters, scopes, spectrums), use the ISender pattern:

SenderControlUse 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 customGeneric data

Setup steps:

  1. Declare sender in plugin .h: IPeakSender<2> mMeterSender;
  2. Define control tag: enum ECtrlTags { kCtrlTagMeter = 0 };
  3. Attach control with tag: pGraphics->AttachControl(new IVMeterControl<2>(bounds), kCtrlTagMeter);
  4. In ProcessBlock: mMeterSender.ProcessBlock(outputs, nFrames, kCtrlTagMeter);
  5. In OnIdle: mMeterSender.TransmitData(*this);
  6. In OnReset: mMeterSender.Reset(GetSampleRate()); (for senders that need it)

For architecture details and spectrum analyzer bidirectional messaging, consult references/visualizers-and-isender.md.

Control Communication

  • Parameter linking: Controls linked to a paramIdx automatically sync with the plugin parameter.
  • Tag lookup: pGraphics->GetControlWithTag(kCtrlTag) for direct access.
  • Groups: Pass group name as 3rd arg to AttachControl, then ForControlInGroup("name", func) or HideControl(paramIdx, bool).
  • Action functions: control->SetActionFunction([](IControl* pCaller) { ... }) for click callbacks.
  • Animation end: SetAnimationEndActionFunction(func) runs after splash/animation completes.
  • Delegate messages: SendControlMsgFromDelegate(ctrlTag, msgTag, dataSize, pData) for plugin-to-control messages.

Custom Controls

Decision tree:

  • Built-in control exists for the need -> use it
  • Custom drawing, no interaction -> ILambdaControl
  • Custom drawing + interaction -> subclass IControl
  • Want IVStyle theming -> subclass IControl + mix in IVectorBase
  • Container with children -> subclass IContainerBase

For custom control patterns, layer caching, drawing primitives, Skia vs NanoVG, and animation, consult references/custom-controls.md.

Key Source Files

ConceptFile
IRECT, IColor, IText, IVStyleIGraphics/IGraphicsStructs.h
IControl, IVectorBase, IBitmapBaseIGraphics/IControl.h
All built-in controlsIGraphics/Controls/IControls.h
ISender, IPeakSender, ISpectrumSenderIPlug/ISender.h
Yoga flexboxIGraphics/Extras/IGraphicsFlexBox.h
VS Code snippets.vscode/IGraphics.code-snippets
Minimal exampleExamples/IPlugEffect/
Intermediate exampleExamples/IPlugInstrument/
Comprehensive showcaseExamples/IPlugControls/
Visualizer exampleExamples/IPlugVisualizer/