Skip to content

Panel UI (NpUi)

NpUi is a small builder that describes panel content. The plugin only lists rows, and the editor draws them with controls that look the same as the built-in panels. You do not need to know Godot.

NpUi is used in two places.

Registration pointFieldWhere it appears
AddContentTabBuildBottom area panel (open from the Window menu)
AddModePanelTools panel while in that mode

Every method returns the same NpUi, so you chain calls with .. Rows are laid out from top to bottom in the order you write them.

MethodWhat is drawnOn change
Title(string text)Bold group title—
Label(string text)One line of text (wraps)—
Label(Func<string> text)Live text. Reread every 0.5 seconds while visible—
Button(string text, Action click, string tooltip = "")Buttonclick when pressed
Text(string label, string initial, Action<string> changed)Name + text fieldchanged on every keystroke
Number(string label, double initial, double min, double max, double step, Action<double> changed)Name + number field (up/down buttons)changed every time the value changes
Check(string label, bool initial, Action<bool> changed)Checkboxchanged when toggled
Separator()Horizontal separator—

Label(() => …) calls the function again every 0.5 seconds while the panel is visible and updates the text.

  • It is also reread immediately when the panel becomes visible again and right after a button is pressed.
  • The function is called every 0.5 seconds. Keep it light. Do not include computations that scan the whole world (Bounds · Blocks()).
  • Show light values such as CountBlocks(), or values you keep in your own fields.
  • If the function throws, the plugin is isolated.
Registration pointWhen it is built
Build of AddContentTabOnce, when the plugin is enabled
Panel of AddModeOnce, when that mode panel is first shown

A panel, once built, is not rebuilt. So initial for Number · Text · Check is the value at the moment it is built. Show values that change later with Label(() => …). To use saved settings as a field’s initial value, read Settings first at the start of Register.

private int _width = 3;
private bool _lamps = true;
private int _built;
public void Register(INpEditorHost host)
{
_width = host.Settings.GetInt("width", 3); // read first so the field's initial value is correct
_lamps = host.Settings.GetBool("lamps", true);
host.AddContentTab(new NpContentTab("panel", "도로", () => new NpUi()
.Title("도로 만들기")
.Number("폭", _width, 1, 9, 1, v => { _width = (int)v; host.Settings.SetInt("width", _width); })
.Check("가로등", _lamps, v => { _lamps = v; host.Settings.SetBool("lamps", v); })
.Label(() => $"깐 도로 {_built}개 · 월드 블록 {host.World.CountBlocks():N0}")
.Separator()
.Button("선택 영역에 깔기", () =>
{
if (host.World.Selection is not { } b) { host.Message("먼저 영역을 고르세요"); return; }
_built++;
// … lay the road with World.Edit …
}, "선택 상자 바닥에 도로를 깝니다")));
}

To see the panel, turn on 도로 in the Window menu.

  • Do not put emoji or pictographic characters in on-screen text. Pictographic characters in the panel title (the Label field) are stripped on screen.
  • Callbacks (click · changed) are called on the editor main thread. You can call World.Edit from them.
  • If a callback throws, the plugin is isolated. The panel disappears as the plugin is unloaded.
  • Icon buttons, collapsible groups, lists and color pickers are not available yet.
  • Text that is not in the editor’s translation table is not translated and appears as-is. If you need multiple languages, the plugin chooses the text itself.