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.CorePresetEditorTheme.EntanglePresetEditorTheme.VoicePreset
Current palette intent:
Core: infrastructure, tooling, framework-level configurationEntangle: world/state/entity/room workflowsVoice: 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
GUIStyleobjects everyOnInspectorGUI()
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:
EditorThemeEditorUIFrameworkInspectorBase<T>and related bases
Current Migration Pattern
The refactor already moved the main duplicated inspectors to the shared framework path.
Examples:
FigNetConfigurationEditor.csEntangleConfigEditor.csFigNetVoiceConfigurationEditor.csEntangleManagerEditor.csVoipConnectionManagerEditor.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.CorePresetEditorTheme.EntanglePresetEditorTheme.VoicePreset
If a new module needs a new identity:
- add a new accent color to
EditorTheme.Palette - add a matching
InspectorPreset - 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:
- header
- 2-5 main sections
- optional summary card
- 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
EditorStylesCachefor shared styles - use
EditorThemefor 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:
FigNetEntangleFigNet.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:
- keep behavior first, appearance second
- move them to the shared base class when possible
- replace local styles with
EditorTheme - replace repeated rows with shared
EditorUIhelpers - 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
Recommended Future Work
- continue migrating remaining legacy
CommonEntangleElementsandCommonFNVoiceElementsusage - 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
EditorUIhelpers? - 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
EditorUIname shadowing?
If the answer to any of these is no, prefer fixing that before adding more custom inspector code.