Skip to content
QinShenYu edited this page Jun 10, 2026 · 3 revisions

Namespaces: ScavLib.gui.ugui (uGUI) | ScavLib.gui.imgui (IMGUI)

ScavLib ships two parallel GUI stacks:

  • uGUI (ScavLib.gui.ugui) — the primary, player-facing UI. Built on Unity's UI / TextMeshPro, themeable, draggable, and able to block game-world clicks. Use this for menus your players actually interact with.
  • IMGUI (ScavLib.gui.imgui) — a lightweight debug / development immediate-mode window stack. Quick to throw together for testing, but not intended as the shipping UI for a polished mod.

Both work in the main menu and in-game. When in doubt, build player-facing features with uGUI and reserve IMGUI for debug panels.

Renamed in 0.6.0: The old ScavLib.gui IMGUI types (MenuWindow, MenuBuilder, MenuManager) were moved into ScavLib.gui.imgui and renamed to ImguiWindow, ImguiMenuBuilder, ImguiMenuManager. See the Changelog for migration notes.


uGUI (Recommend for Player-Facing)

Creating a Window

Inherit UguiWindowBase and implement Build(UguiBuilder). Controls are added once at build time, each wired to a callback — unlike IMGUI, you do not redraw every frame.

using ScavLib.gui.ugui;
using ScavLib.util;
using UnityEngine;

public class MyModUguiMenu : UguiWindowBase
{
    public override string Title => "My Mod";
    public override KeyCode ToggleKey => KeyCode.F5;

    protected override void Build(UguiBuilder b)
    {
        b.AddLabel("Player Controls");
        b.AddSeparator();

        b.AddButton("Heal All", () => PlayerUtil.HealAll());

        b.AddToggle("God Mode", false, on =>
        {
            GameUtil.Log($"God mode: {on}");
        });

        b.AddSlider("Heal Amount", 0f, 100f, 50f, v =>
        {
            GameUtil.Log($"Heal amount set to {v:F0}");
        });
    }
}

Register it in your plugin's Awake():

private void Awake()
{
    UguiWindowBase.Register(new MyModUguiMenu());
}

The window builds lazily the first time it is shown, then toggles with F5.


UguiWindowBase Members

Member Type Default Description
Title string Title bar text. An empty title hides the title bar.
ToggleKey KeyCode KeyCode.None Hotkey to show/hide. None disables hotkey toggling.
Width float Theme.Metrics.WindowDefaultSize.x (400) Window width. Override for a fixed width.
Height float Theme.Metrics.WindowDefaultSize.y (300) Window height. Override for a fixed height.
Theme UguiTheme UguiTheme.Default Visual theme. Override to reskin.
ShowInMenu bool false If true, the window is also usable in the main menu (no PlayerCamera).
DefaultDraggable bool true Initial draggable state.
Draggable bool (read-only) Current draggable state. Change at runtime via SetDraggable.
DefaultBlockGameInput bool true Initial click-blocking state.
BlockGameInput bool Runtime flag. When true, a click on this window does not pass through to the game world.
IsVisible bool (read-only) false Current visibility.
Root RectTransform (read-only) The built window root. null until first shown.

Methods

Method Description
Build(UguiBuilder builder) Abstract. Implement to populate the window with controls.
Show() Build (if needed) and show the window.
Hide() Hide the window.
Toggle() Toggle visibility.
SetDraggable(bool) Enable/disable dragging at runtime.
OnShow() / OnHide() Optional overrides called when the window is shown/hidden.
UguiWindowBase.Register(window) Static. Register a window so its hotkey is polled. Call in Awake().

Click-blocking: While a visible window has BlockGameInput = true, clicking on it will not trigger world-space actions (item pickup, etc.). Clicks on empty space still pass through. Set it to false for purely informational HUD overlays that should not eat input.

Close button: Windows show an X close button in the top-right of the title bar by default. It is suppressed automatically when the title bar is hidden. Its size and right-edge padding are theme metrics (see UguiTheme).


UguiBuilder Controls

All controls are added inside Build(). Each returns the underlying Unity component (or a ScavLib handle) so you can hold a reference and update it later (e.g. an AddProgressBar handle whose Fill you set each frame from your own code, or an AddDropdown handle you drive with SetIndex).

Display

Method Returns Description
AddLabel(string text) TextMeshProUGUI A non-interactive text row.
AddImage(Sprite sprite, float pixelSize = 3f, float? maxSize = null) Image Display a sprite, sized from its texture by pixelSize (pixel multiplier) capped at maxSize. Non-interactive by default. Returns null for a null sprite.
AddImage(string spriteName, float pixelSize = 3f, float? maxSize = null) Image Same as above, but looks the sprite up by resource name (exact match first, then contains).
AddSeparator() void A horizontal divider row.
AddSpace(float pixels = -1f) void Vertical empty space. Negative uses the theme's default spacing.

Interaction

Method Returns Description
AddButton(string label, Action onClick) Button A button; onClick fires when clicked.
AddToggle(string label, bool initial, Action<bool> onChange = null) Toggle A checkbox; onChange receives the new value.
AddSlider(string label, float min, float max, float initial, Action<float> onChange = null) Slider A labelled slider. The label auto-updates with the current value.
AddInputField(string initialText = "", string placeholder = "", Action<string> onValueChanged = null, Action<string> onEndEdit = null) TMP_InputField A single-line text field. onValueChanged fires each keystroke; onEndEdit fires on focus loss / Enter.
AddNumberField(float initial = 0f, float step = 1f, float min = float.MinValue, float max = float.MaxValue, bool isInteger = false, Action<float> onChange = null) UguiNumberField A numeric stepper [ - ][ field ][ + ]. Clamps to min/max; isInteger rounds to whole numbers.
AddDropdown(IList<string> options, int initialIndex = 0, Action<int> onChange = null) UguiDropdown A self-contained dropdown. Options expand on a global overlay and collapse on outside click.
AddToggleGroup(IList<string> options, int initialIndex = 0, UguiToggleGroupStyle style = Checkbox, Action<int> onChange = null) UguiToggleGroup A mutually exclusive selector. Checkbox renders one labelled checkbox per row; Buttons renders a single row of equal-width buttons.

Containers

Method Returns Description
AddProgressBar(float initial01 = 0f) UguiPixelBar A pixel-snapped fill bar (0~1). Set .Fill to update.
AddScrollView(float height) (ScrollRect, RectTransform) A vertical scroll area with a scrollbar. Parent further content under the returned RectTransform.
BeginHorizontal(float rowHeight = -1f) UguiHorizontalRow Begin a horizontal row to place controls side by side. See Layout Containers.
BeginTabs() UguiTabView Begin a tabbed view. See Layout Containers.

Note on slider arguments: uGUI's AddSlider takes (label, min, max, initial) — min/max come before the initial value. This differs from IMGUI's Slider(label, value, min, max).

protected override void Build(UguiBuilder b)
{
    var hpBar = b.AddProgressBar(1f);

    b.AddButton("Take 10 Damage", () =>
    {
        float hp = PlayerUtil.GetBrainHealth();
        hpBar.Fill = Mathf.Clamp01((hp - 10f) / 100f);
    });

    var name = b.AddInputField("", "Enter a name...", onEndEdit: v =>
        GameUtil.Log($"Name set to {v}"));

    var qty = b.AddNumberField(1f, step: 1f, min: 0f, max: 99f, isInteger: true,
        onChange: v => GameUtil.Log($"Quantity: {v}"));

    var mode = b.AddDropdown(new[] { "Easy", "Normal", "Hard" }, 1, i =>
        GameUtil.Log($"Mode index {i}"));

    var (scroll, content) = b.AddScrollView(120f);
    // Build additional rows under `content` with another UguiBuilder if needed.
}

Returned Control Handles

A few controls return ScavLib handles rather than raw Unity components.

UguiNumberField

Member Description
Value Read-only current value.
Step(int direction) Apply one increment (+1) or decrement (-1) of the configured step.
SetValue(float v, bool notify) Set the value programmatically (clamped/rounded). Pass notify: false to suppress onChange.

UguiDropdown

Member Description
Index Read-only selected index.
SelectedText Read-only text of the current selection.
SetIndex(int index, bool notify = true) Select an option programmatically.

UguiToggleGroup

Member Description
SelectedIndex Read-only selected index.
SetSelected(int index, ...) Select an option programmatically.

UguiToggleGroupStyle has two values: Checkbox (one labelled checkbox per row) and Buttons (a single row of equal-width buttons).


Layout Containers

Horizontal Rows

BeginHorizontal() returns a UguiHorizontalRow that places controls left to right within one row. It implements IDisposable, so a using block settles the row (pushes its height back to the layout) automatically.

Method Description
AddSmallButton(string label, Action onClick, float fixedWidth) A fixed-width small button.
AddSmallButton(string label, Action onClick) A small button sized to fit its text.
AddFlexibleSmallButton(string label, Action onClick) Registers a button that shares the row's remaining width equally with other flexible buttons (laid out when the row settles).
End() / Dispose() Settle the row. Called automatically at the end of a using block.
protected override void Build(UguiBuilder b)
{
    using (var row = b.BeginHorizontal())
    {
        row.AddSmallButton("Save", () => GameUtil.Log("save"));
        row.AddSmallButton("Load", () => GameUtil.Log("load"));
        row.AddFlexibleSmallButton("Reset", () => GameUtil.Log("reset"));
    }
}

Tabs

BeginTabs() returns a UguiTabView. Add pages with AddTab(name, buildPage), where buildPage receives a fresh UguiBuilder for that page's content. It is also IDisposable; a using block defaults to the first page and completes initial layout on exit.

Method Description
AddTab(string name, Action<UguiBuilder> buildPage) Add a page: tab label + a callback that builds the page contents.
Select(int index) Show the page at index, hide the rest, and re-layout.
ActiveIndex Read-only current page index (-1 before the first selection).
End() / Dispose() Settle the view (selects the first page). Called automatically at the end of a using block.
protected override void Build(UguiBuilder b)
{
    using (var tabs = b.BeginTabs())
    {
        tabs.AddTab("General", page =>
        {
            page.AddLabel("General settings");
            page.AddToggle("God Mode", false);
        });

        tabs.AddTab("Advanced", page =>
        {
            page.AddSlider("Spawn Rate", 0f, 10f, 1f);
        });
    }
}

Tooltips & Overlay Layer

A shared overlay layer backs both tooltips and dropdowns. It renders on a single persistent canvas that stays above open windows and survives scene transitions, so tooltips and expanded dropdowns are never clipped by a window.

Attach a hover tooltip to any control with the chainable WithTooltip extension. It works on the value returned by any Add... call (any Component) as well as on a GameObject, and returns the same object so you can keep chaining.

b.AddButton("Heal All", () => PlayerUtil.HealAll())
 .WithTooltip("Heal All", "Restores every limb and clears status effects.");

b.AddToggle("God Mode", false)
 .WithTooltip("God Mode");   // name only; description is optional

ScavLib's tooltips are deliberately isolated from the game's native tooltip system. A control carrying a ScavLib tooltip will not also trigger a duplicate game-native tooltip.


UguiTheme

UguiTheme is an immutable bundle of all visual/layout parameters. Use UguiTheme.Default for the stock look (matches the game's native style), or derive a variant with chainable With... methods. Each call returns a new theme; the original (including Default) is never mutated.

public override UguiTheme Theme =>
    UguiTheme.Default
        .WithWindowSize(500f, 360f)
        .WithRowSpacing(8f)
        .WithTitleAlignment(UguiTheme.TitleAlign.Center);

Common knobs

Method Description
WithWindowSize(width, height) Default window size (used when Width/Height are not overridden).
WithRowHeight(float) Height of each control row.
WithRowSpacing(float) Gap between control rows.
WithDefaultSpace(float) Default pixel size used by AddSpace().
WithHeaderHeight(float) Title bar height.
WithContentPadding(float) / (l, r, t, b) Inner padding between the window edge and the content area.
WithPanelColor(Color) Fallback panel background color (header follows it).
WithHeaderBg(Color) Header background color only (advanced).
WithHiddenTitle(bool = true) Hide the entire title bar (highest priority).
WithTitleAlignment(TitleAlign) Left / Center / Right title alignment.
WithTitlePadding(float) / (l, r, t, b) Title text padding (independent of content padding).

Title knob priority (high → low): HideTitle > TitleAlignment > TitlePadding. Hiding the title disables alignment and all title padding; non-Left alignment disables left/right title padding (top/bottom still apply). Title padding and content padding are two independent sets of fields.

Layout metrics

Beyond the knobs above, UguiTheme.Metrics carries the layout values that drive the full control set — small-button sizing, tab header height, input-field height and padding, stepper-button width, dropdown arrow width / row height / max visible rows, tooltip width / padding / cursor offset, toggle-group button height, default image max size, scrollbar width, and the title-bar close-button size / right padding (with ShowCloseButton enabled by default). These match sensible internal defaults that mirror the game's native look.

Font choices, nine-slice sprite names, outline distances, and checkmark/slider pixel metrics are locked to sensible internal defaults and are not exposed through With... methods.


UguiPixelBar

The handle returned by AddProgressBar. Set Fill (clamped 0~1) to update the bar; when backed by a sprite it snaps to whole texture pixels for a crisp, retro look.

var bar = builder.AddProgressBar(0.5f);
bar.Fill = 0.75f;

IMGUI (Recommend for Debug)

A lightweight immediate-mode window stack for debug panels. Inherit ImguiWindow, draw controls with ImguiMenuBuilder inside DrawContent(), and register with ImguiMenuManager. Unlike uGUI, DrawContent() runs every frame and interactive controls are polled by return value.

Creating a Window

using ScavLib.gui.imgui;
using ScavLib.util;
using UnityEngine;

public class MyModDebugMenu : ImguiWindow
{
    public override string Title => "My Mod (Debug)";
    public override KeyCode ToggleKey => KeyCode.F6;
    public override float Width => 280f;
    // Height defaults to 0, which enables true auto-height.

    protected override void DrawContent()
    {
        ImguiMenuBuilder.Label($"Hunger: {PlayerUtil.GetHunger():F1}");
        ImguiMenuBuilder.Separator();

        if (ImguiMenuBuilder.Button("Heal All"))
            PlayerUtil.HealAll();

        if (ImguiMenuBuilder.Button("Max Hunger"))
            PlayerUtil.Feed(100f);
    }
}

Register in Awake():

private void Awake()
{
    ImguiMenuManager.Register(new MyModDebugMenu());
}

ImguiWindow Properties

Property Type Default Description
Title string Title bar text.
ToggleKey KeyCode KeyCode.None Hotkey to show/hide.
Width float 300f Window width in pixels.
Height float 0f 0 = auto-height (resizes to content). Set a value to fix it.
InitialPosition Vector2 Screen center Starting position on first render.
Layer int 0 Render order. Lower value renders on top.
ShowInMenu bool false If true, also active in the main menu.
IsVisible bool (read-only) false Current visibility.

Methods

Method Description
DrawContent() Abstract. Draw the window's controls here (called every frame).
Show() / Hide() / Toggle() Visibility control.
OnShow() / OnHide() Optional overrides on open/close.

ImguiMenuBuilder Controls

All methods must be called from within DrawContent().

Display

Method Description
Label(string text) Non-interactive text label.
Separator() Horizontal divider line.
Space(float pixels = 8f) Vertical empty space.

Interaction

Method Returns Description
Button(string label) bool true on the frame it is clicked.
Toggle(string label, bool value) bool Checkbox. Returns the new value.
Slider(string label, float value, float min, float max) float Slider with label. Returns the new value.

BepInEx Config Binding

Method Description
SliderConfig(string label, ConfigEntry<float> entry, float min, float max) Slider bound directly to a float config entry.
ToggleConfig(string label, ConfigEntry<bool> entry) Toggle bound directly to a bool config entry.

Layout

Method Description
BeginHorizontal() / EndHorizontal() Horizontal layout group.
BeginScroll(ref Vector2 scrollPos) / EndScroll() Scrollable area.
private Vector2 _scroll;

protected override void DrawContent()
{
    ImguiMenuBuilder.BeginScroll(ref _scroll);
    foreach (var id in ItemUtil.GetAllIds())
        ImguiMenuBuilder.Label(id);
    ImguiMenuBuilder.EndScroll();
}

Window ID Management

ImguiMenuManager assigns a unique IMGUI window ID (starting at 9000) to each registered window automatically. Do not assign window IDs manually.

Console Conflict Prevention

IMGUI hotkey detection is suspended while the developer console is open, so menu toggle keys will not fire accidentally during console input.

Clone this wiki locally