
SparrohUILib
Shared UI library for Sparroh Mycopunk mods. Provides themed widgets, HUD builders, windows, and resolution-aware layout.SparrohUILib
Shared UI library for Sparroh Mycopunk mods. One theme, one set of widgets, resolution-aware layout.
Features
-
Theme system — Mycopunk-inspired teal/slate surfaces with bioluminescent accents
-
Resolution scaling — Reference 1920×1080; scales cleanly across aspect ratios via
CanvasScaler+UITheme.Scale -
HUD builder — Single- and multi-line HUD text under the player reticle (normalized anchors); respects vanilla Hide HUD
-
HUD reposition — Soft-dependency helpers for ModSettingsMenu drag-reposition (
HudAnchors,HudRepositionClient,HudHandle.EnableReposition) -
Gear action bar — Shared top toolbar on the gear/menu canvas for cross-mod action buttons
-
Widgets — Text, Button, Toggle, InputField, Panel, ScrollView, Separator, Dropdown, Slider, ProgressBar, Tabs, Tooltip, DragList
-
Windows & dialogs — Overlay windows (per-window canvas), confirm/alert dialogs
-
Rich text helpers —
Label: value unitformatting with colored values -
Config colors — Bind hex color config entries with cached
Colorvalues
Install
Thunderstore / r2modman: install Sparroh-SparrohUILib as a dependency of your mod.
Manual: place SparrohUILib.dll in BepInEx/plugins/.
Consumer setup
csproj
<Reference Include="SparrohUILib">
<HintPath>..\SparrohUILib\bin\Release\netstandard2.1\SparrohUILib.dll</HintPath>
</Reference>
Plugin
[BepInDependency("sparroh.uilibrary")]
[BepInPlugin(...)]
public class MyPlugin : BaseUnityPlugin { }
thunderstore.toml
[package.dependencies]
BepInEx-BepInExPack_Mycopunk = "5.4.2403"
Sparroh-SparrohUILib = "1.2.0"
Quick examples
using Sparroh.UI;
// HUD anchors (config) + rebuild when the handle dies after quit-to-menu
var anchors = HudAnchors.Bind(Config, "Altimeter", 0.15f, 0.84f);
if (!HudHandle.IsValid(hud))
{
hud = HudBuilder.Create("AltimeterHUD")
.ParentToReticle()
.Anchor(anchors.XValue, anchors.YValue)
.Size(300, 25)
.AddText("AltitudeText")
.Build();
// Soft-dep ModSettingsMenu: drag in F9 mode, SettingChanged → SetAnchor, auto-unregister on destroy
if (HudHandle.IsValid(hud))
hud.EnableReposition("my.mod.guid", "Altimeter", anchors);
}
if (HudHandle.IsValid(hud))
hud.Primary.SetRich("Altitude", 12.3f, UIColors.Shamrock, "m");
// Multi-line HUD
var meter = HudBuilder.Create("Carnometer")
.ParentToReticle()
.Anchor(0.15f, 0.95f)
.Size(320, 100)
.AddLines(4)
.Build();
meter.Lines[0].SetRichWithRate("Total Damage", total, dps, UIColors.Rose);
// Overlay window
var window = UIWindow.Create("Settings", new Vector2(800, 600), "Mod Settings", scrollable: true);
UIWindow.CreateSectionHeader(window.Content, "General");
UIToggle.Create(window.Content, "Enable HUD", true, on => { /* ... */ });
UISlider.Create(window.Content, "Opacity", 0f, 1f, 0.8f, v => { /* ... */ });
UIButton.Create(window.Content, "Save", () => { /* ... */ }, UIButtonStyle.Primary);
// Dialogs
UIDialog.Confirm("Scrap upgrades?", "This cannot be undone.", onConfirm: DoScrap);
UIDialog.Alert("Done", "Upgrades scrapped.");
// Tooltip
UITooltip.Attach(someButton.GameObject, "Does the thing");
// Gear menu action bar (library hosts/ticks the bar)
GearActionBar.Register("mymod.scrap", "Scrap", GearActionBar.OrderScrapMarked, DoScrap, UIButtonStyle.Danger);
Theme & scaling
| API | Purpose |
|---|---|
UIColors.* |
Palette (Sky, Rose, Shamrock, PanelBg, ButtonPrimary, …) |
UIColors.TryParseHex / ParseHex |
Parse RRGGBB / #RRGGBB / RRGGBBAA / #RRGGBBAA |
UIColors.ToHex / WithAlpha |
Convert Color ↔ hex; adjust alpha |
ConfigColor.Bind(...) |
Bind a hex color config entry with cached Color |
UITheme.S(px) / UITheme.Scale |
Scale reference pixels to current resolution |
UITheme.ScaledSize(w, h) |
Scaled Vector2 |
UITheme.ClampToScreen(size) |
Keep windows on-screen |
RichText.Labeled(...) |
Colored label/value strings |
Configurable HUD colors
// In your mod constructor:
valueColor = ConfigColor.Bind(Config, "Colors", "ValueColor", UIColors.Sky,
"Rich-text value color (hex RRGGBB or #RRGGBB).");
// When drawing:
hud.Primary.SetRich("Speed", speed, valueColor.Value, "m/s");
HUD positions use normalized anchors (0–1) so they stay consistent across resolutions. Window canvases use CanvasScaler with reference 1920×1080 and match width/height 0.5.
Vanilla Hide HUD
Gameplay HUDs created with HudBuilder automatically hide when the player enables the vanilla Hide HUD option (PlayerLook.DisablePlayerHUD). Call hud.SetActive(yourConfigEnabled) as usual — the library combines your desired state with the vanilla toggle.
// Optional: read the vanilla toggle yourself (e.g. custom non-HudHandle UI)
if (HudVisibility.IsHidden) { /* skip drawing */ }
Menu overlays (UIWindow, GearActionBar) are not affected.
Scene transitions (quit to menu / lobby)
Reticle-parented HUD is destroyed with the player when you quit to menu. The C# HudHandle wrapper is not a MonoBehaviour, so you must treat a dead handle as missing and rebuild:
// Every frame (or whenever you would create/update HUD):
if (!HudHandle.IsValid(hud))
{
hud = null;
hud = HudBuilder.Create("MyHUD")
.ParentToReticle()
.Anchor(anchors.XValue, anchors.YValue)
.Size(300, 25)
.AddText()
.Build();
// EnableReposition auto-unregisters when the old handle dies; call again after rebuild
if (HudHandle.IsValid(hud))
hud.EnableReposition("my.mod.guid", "My HUD", anchors);
}
if (!HudHandle.IsValid(hud))
return; // player/reticle not ready yet
hud.Primary.SetRich("Speed", speed, UIColors.Sky, "m/s");
hud.IsAlive/HudHandle.IsValid(hud)become false after scene unload- Do not use
if (hud != null) returnalone in your create helper — a destroyed handle is still a non-null C# object - Reposition bindings detach automatically on destroy / scene unload; re-call
EnableRepositionafter a successful rebuild GearActionBarrebuilds its host under the live Menu canvas automatically (library ticks it each frame)
Gear action bar
Shared button row for the gear details menu. Hosted on the game Menu canvas (camera/blit UI space) so hitboxes match vanilla menu chrome. The library builds, re-parents, and ticks the bar — consumers only register slots.
GearActionBar.Register(
id: "mymod.clear",
label: "Clear",
order: GearActionBar.OrderClearGrid,
onClick: OnClear,
style: UIButtonStyle.Default);
GearActionBar.SetText("mymod.clear", "Clear All");
GearActionBar.SetInteractable("mymod.clear", canClear);
GearActionBar.SetSlotVisible("mymod.clear", showClear);
GearActionBar.Unregister("mymod.clear");
Use the shared GearActionBar.Order* constants so buttons from different mods sort consistently. Call GearActionBar.SetContextVisible(true/false) when your gear UI opens/closes if you drive visibility yourself.
HUD repositioning
Drag mode (default F9) lives in ModSettingsMenu. SparrohUILib provides the consumer-side helpers so each mod does not need a copied reflection client.
// 1) Bind anchor config (writes [HUD Positioning] / "Altimeter X" + "Altimeter Y")
var anchors = HudAnchors.Bind(Config, "Altimeter", defaultX: 0.15f, defaultY: 0.84f);
// 2) After a successful HudBuilder.Build():
hud.EnableReposition("your.mod.guid", "Altimeter", anchors);
What EnableReposition does:
- Soft-calls ModSettingsMenu
HudRepositionAPIvia reflection (no hard dependency) - Applies
SetAnchorwhen the config entries change (including after a drag-save) - Unregisters and drops listeners when the handle is destroyed or
DisableReposition()is called
Lower-level APIs (same soft dependency):
| API | Purpose |
|---|---|
HudRepositionClient.Register / Unregister |
Direct register with rect + ConfigEntrys |
HudRepositionClient.IsAvailable |
Whether ModSettingsMenu API was found |
HudReposition.Bind / Unbind |
Same as EnableReposition / DisableReposition |
HudAnchors.BindKeys |
Custom key names (e.g. FooAnchorX / FooAnchorY) |
Convention for auto-detect in ModSettingsMenu: section [HUD Positioning], float keys ending in AnchorX / AnchorY, or the {Name} X / {Name} Y keys used by HudAnchors.Bind. Parent HUD under the player reticle.
License
MIT — see LICENSE