Skip to main content

Unity Inspector Framework

Purpose

This document describes the shared Unity custom inspector framework used by FigNet, Entangle, and FnVoice.

The refactor moved the editor UI system from module-specific helpers into one shared foundation so future inspector work can:

  • reuse one visual language
  • avoid duplicated helper classes and styles
  • reduce editor-time allocations and repaint churn
  • keep new modules aligned with the existing inspector experience

This is an editor-only architecture. It does not change runtime networking behavior.

Primary Goals

  • One shared theme system across modules
  • One shared set of drawing helpers
  • One shared set of editor base classes
  • Minimal per-inspector boilerplate
  • Compatibility for older inspectors during migration

Framework Location

The shared framework lives in:

  • FigNetCommonElements.cs

Compatibility shim for older Entangle inspectors:

  • EditorUIHelper.cs

Core Pieces

EditorTheme

EditorTheme is the single source of truth for:

  • module accent colors
  • surface and outline colors
  • text colors
  • spacing constants
  • visual presets

Current presets:

  • EditorTheme.CorePreset
  • EditorTheme.EntanglePreset
  • EditorTheme.VoicePreset

Current palette intent:

  • Core: infrastructure, tooling, framework-level configuration
  • Entangle: world/state/entity/room workflows
  • Voice: live communication and media-related flows

Do:

  • add new module accents here
  • tune colors centrally here
  • keep semantic colors consistent (Success, Warning, Danger, Info)

Do not:

  • define one-off hardcoded color systems in individual inspectors

EditorStylesCache

EditorStylesCache owns cached GUIStyle instances.

It exists to prevent repeated style allocation during inspector repaint.

Use it for:

  • header label styles
  • foldout styles
  • chip text styles
  • compact button styles

Do:

  • initialize through EditorStylesCache.Initialize()
  • add shared styles here if multiple inspectors need them

Do not:

  • create new GUIStyle objects every OnInspectorGUI()

EditorUI

EditorUI is the shared drawing helper layer.

Key helpers include:

  • DrawInspectorHeader(...)
  • DrawSectionHeader(...)
  • DrawListItemHeader(...)
  • DrawSummaryCard(...)
  • DrawStatusRow(...)
  • DrawReadonlyRow(...)
  • DrawSummaryLine(...)
  • DrawInfoBox(...)
  • DrawMessageMetric(...)
  • DrawPropertySlot(...)
  • DrawEntityState(...)
  • DrawStringEnumPopup<T>(...)

Use EditorUI for all repeated visual patterns before adding custom drawing code.

SerializedPropertyExtensions

Shared helpers for property access and repetitive property drawing.

Current examples:

  • FindRequiredRelative(...)
  • string-backed enum popup helpers

When repeated SerializedProperty patterns appear in multiple inspectors, add them here instead of duplicating the logic.

Base Editor Types

The framework exposes shared bases for common inspector categories.

FrameworkInspectorBase<T>

General-purpose base for most inspectors.

Use when:

  • the inspector is not specifically a network manager or a configuration asset
  • you want shared header/summary/section helpers

Provides:

  • typed target access
  • preset-driven header rendering
  • section helpers
  • summary card helpers
  • optional live repaint support

ConnectionManagerInspectorBase<T>

Use for runtime manager views such as connection/session/room managers.

Use when:

  • the inspected object exposes connection state or runtime networking stats
  • the inspector needs a standardized manager summary layout

NetworkConfigurationEditorBase<T>

Use for configuration assets that share common networking fields.

Use when:

  • the object is a config asset
  • it has shared core fields such as app id, server ip, port, security, sync, queues, fallback, or transport settings

This base reduces repeated property caching and repeated section layout across configuration editors.

Compatibility Layer

EditorUIHelper remains as a compatibility shim for older Entangle editor code.

It forwards into the shared framework and should be treated as transitional.

Use it only when:

  • updating a legacy inspector incrementally
  • avoiding a large migration in one pass

Do not use it for brand-new inspectors.

For new work, use:

  • EditorTheme
  • EditorUI
  • FrameworkInspectorBase<T> and related bases

Current Migration Pattern

The refactor already moved the main duplicated inspectors to the shared framework path.

Examples:

  • FigNetConfigurationEditor.cs
  • EntangleConfigEditor.cs
  • FigNetVoiceConfigurationEditor.cs
  • EntangleManagerEditor.cs
  • VoipConnectionManagerEditor.cs

Use these as reference implementations for future work.

How To Build a New Inspector

Option 1: Standard Inspector

Use FrameworkInspectorBase<T>.

Example pattern:

using FigNet.EditorUIFramework;
using UnityEditor;

[CustomEditor(typeof(MyComponent))]
public class MyComponentEditor : FrameworkInspectorBase<MyComponent>
{
private bool _showMain = true;

protected override InspectorPreset Preset => EditorTheme.CorePreset;

public override void OnInspectorGUI()
{
serializedObject.Update();

DrawHeader("My Component", "Short module-specific subtitle");

if (DrawSection("Main Settings", ref _showMain, "Explain what this section controls", EditorTheme.Palette.Core))
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(serializedObject.FindProperty("SomeField"));
EditorGUI.indentLevel--;
}

CompleteInspector();
}
}

Option 2: Configuration Asset

Use NetworkConfigurationEditorBase<T>.

Override only the module-specific parts:

  • Preset
  • title/subtitle labels
  • extra property caching
  • advanced/fallback/custom validation sections

This should be the default path for new network configuration assets.

Option 3: Runtime Manager Inspector

Use ConnectionManagerInspectorBase<T>.

This keeps runtime manager inspectors visually aligned and avoids custom summary/status layouts for every module.

Design Rules

1. Use presets, not local themes

New inspectors should pick one preset:

  • EditorTheme.CorePreset
  • EditorTheme.EntanglePreset
  • EditorTheme.VoicePreset

If a new module needs a new identity:

  1. add a new accent color to EditorTheme.Palette
  2. add a matching InspectorPreset
  3. use that preset from the inspector

Do not create local color systems per inspector.

2. Use section cards consistently

Use DrawSection(...) from the base classes or EditorUI.DrawSectionHeader(...).

Each inspector should generally follow:

  1. header
  2. 2-5 main sections
  3. optional summary card
  4. optional validation/info boxes

Avoid nested visual noise and too many custom boxes.

3. Use semantic chips and rows

For status and readonly runtime information, prefer:

  • EditorUI.DrawStatusRow(...)
  • EditorUI.DrawReadonlyRow(...)
  • EditorUI.DrawSummaryLine(...)

Do not hand-roll repeated horizontal rows unless the layout is genuinely unique.

4. Keep the UI calmer

The framework intentionally favors:

  • neutral surfaces
  • left accent bars
  • compact chips
  • restrained use of saturation

Avoid:

  • large solid bright panels
  • multiple competing accent colors in one inspector
  • custom gradients for every section

Top-level headers can be visually richer than body sections, but keep them subtle.

Performance Guidelines

These rules matter because inspectors repaint frequently in Unity.

Do

  • use EditorStylesCache for shared styles
  • use EditorTheme for shared colors/spacings
  • only repaint continuously when the inspector is showing live runtime state
  • keep summary and status rendering compact

Do not

  • create textures during every repaint
  • create styles during every repaint
  • call Repaint() unconditionally in static config inspectors
  • allocate lots of temporary objects in hot editor paths

Live Repaint Guidance

Only runtime inspectors should opt into live repaint behavior.

Typical rule:

  • configuration assets: no continuous repaint
  • managers/runtime stats: repaint only when play mode is active and the data is actually live

If using FrameworkInspectorBase<T>, prefer overriding the live repaint behavior centrally instead of calling Repaint() ad hoc inside the inspector body.

Namespace Guidance

Be careful with namespace naming when using the shared EditorUI helper.

Some module namespaces such as:

  • FigNet
  • Entangle
  • FigNet.Voice.EditorUI

can shadow the helper class name if you call EditorUI. directly.

Preferred pattern:

using SharedEditorUI = FigNet.EditorUIFramework.EditorUI;

Then use:

SharedEditorUI.DrawInfoBox(...);

This avoids namespace collisions and makes intent explicit.

Extension Rules

Add to EditorTheme when

  • a new module needs a new accent preset
  • spacing or shared surface rules need to change
  • semantic colors need central adjustment

Add to EditorStylesCache when

  • multiple inspectors need the same GUIStyle

Add to EditorUI when

  • a visual pattern appears in 2 or more inspectors
  • the pattern has design or spacing rules that should stay unified

Add to SerializedPropertyExtensions when

  • the same property lookup/drawing logic appears in multiple editors

Add a new base class when

  • several inspectors share the same structure, not just one helper call

Do not add a base class for one inspector only.

Migration Rules For Older Inspectors

When converting older inspectors:

  1. keep behavior first, appearance second
  2. move them to the shared base class when possible
  3. replace local styles with EditorTheme
  4. replace repeated rows with shared EditorUI helpers
  5. remove custom repaint loops unless truly needed

If a full migration is too large:

  • keep the inspector functional
  • route it through EditorUIHelper
  • convert fully in a later pass
  • continue migrating remaining legacy CommonEntangleElements and CommonFNVoiceElements usage
  • move any remaining duplicated prefab/room/entity summary layouts into EditorUI
  • centralize more list rendering helpers if additional config editors start repeating add/remove/foldout flows
  • consider a small shared icon strategy if the editor theme grows further

Quick Checklist For New Inspector Work

  • Is this using a shared base class?
  • Is the inspector using a preset from EditorTheme?
  • Are repeated rows drawn with EditorUI helpers?
  • Are styles cached instead of created per repaint?
  • Is live repaint enabled only when necessary?
  • Are colors and spacing coming from the shared framework?
  • Is the namespace safe from EditorUI name shadowing?

If the answer to any of these is no, prefer fixing that before adding more custom inspector code.