> For the complete documentation index, see [llms.txt](https://docs.fullscreen.no/info/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.fullscreen.no/info/standalone-assets/runtime-studio/api/user-interface.md).

# User Interface

Runtime Studio provides several extension points for building custom editor UI at runtime. Most UI extensions can be discovered automatically, but registering them through an `IEditorModule` is recommended.

### UI Extension Overview

| API                              | Purpose                                                |
| -------------------------------- | ------------------------------------------------------ |
| `IEditorPanel`                   | Create custom docked or overlay panels.                |
| `SceneToolOverlayItem`           | Add buttons and controls to the Scene View overlay.    |
| `SceneSettingsMenuItem`          | Add entries to the Scene Settings menu.                |
| `IRuntimeComponentDrawer`        | Draw custom inspector UI for Unity components.         |
| `IRuntimePropertyDrawer`         | Draw custom inspector UI for nested data types.        |
| `HierarchyAdornmentDefinition`   | Show icons beside hierarchy items.                     |
| `IRuntimeHierarchyContextAction` | Add hierarchy right-click menu actions.                |
| `FieldInspector`                 | Draw runtime-safe fields inside custom drawers.        |
| `EditorStyles`                   | Create UI that matches Runtime Studio's look and feel. |

***

## Panels

Panels are UI Toolkit views displayed inside the Runtime Studio editor.

{% code expandable="true" %}

```cs
using Fullscreen.RuntimeStudio.Runtime;
using Fullscreen.RuntimeStudio.Runtime.UI;
using UnityEngine.UIElements;

public sealed class NotesPanel : IEditorPanel
{
    public NotesPanel()
    {
        Root = EditorStyles.PanelElement("NotesPanel");

        Root.Add(EditorStyles.Title("Notes"));
        
        Root.Add(new TextField());
    }

    public VisualElement Root { get; }

    public void Refresh() { }
    public void Tick() { }
    public void Dispose() { }
}
```

{% endcode %}

Register the panel:

{% code expandable="true" %}

```cs
public sealed class NotesModule : IEditorModule
{
    public void Register(EditorModuleBuilder builder)
    {
        builder.AddDefaultPanel(
            PanelDefinition.Left(
                "my-game.notes",
                "Notes",
                _ => new NotesPanel()
            )
        );
    }
}
```

{% endcode %}

#### Panel Locations

```cs
PanelDefinition.Left(...)
PanelDefinition.Right(...)
PanelDefinition.Overlay(...)
```

#### Common Options

```cs
.Ordered(100)
.HiddenByDefault()
.Widths(320f, 220f, 500f)
.Heights(400f, 250f, 700f)
.Flexible()
```

## Scene Tool Overlay Items

Scene tool overlay items appear as buttons inside the Scene View.

{% code expandable="true" %}

```cs
builder.AddDefaultSceneToolOverlayItem(
    SceneToolOverlayItem.Button(
        "my-game.spawn",
        context =>
        {
            CreateObject();
        })
    .WithTooltip("Create Object")
    .Ordered(100)
);
```

{% endcode %}

#### Common Options

{% code expandable="true" %}

```cs
.WithLabel("Spawn")
.WithTooltip("Create Object")
.Ordered(100)
.VisibleWhen(context => true)
.EnabledWhen(context => true)
.ActiveWhen(context => false)
```

{% endcode %}

#### Placement

{% code expandable="true" %}

```cs
.Placed(SceneToolOverlayPlacement.TransformTools)
.Placed(SceneToolOverlayPlacement.SceneViewActions)
```

{% endcode %}

## Scene Settings Menu Items

Add custom entries to the Scene Settings dropdown.

{% code expandable="true" %}

```cs
builder.AddDefaultSceneSettingsMenuItem(
    SceneSettingsMenuItem.Button(
        "my-game.rebuild",
        context =>
        {
            RebuildData();
        })
    .WithLabel("Rebuild Data")
);
```

{% endcode %}

Headers:

{% code expandable="true" %}

```cs
SceneSettingsMenuItem.Header(
    "my-game.header",
    "My Tools"
);
```

{% endcode %}

Submenus:

{% code expandable="true" %}

```cs
SceneSettingsMenuItem.Submenu(
    "my-game.options",
    "Options",
    context => BuildItems()
);
```

{% endcode %}

## Component Drawers

Component drawers control how a component appears in the inspector.

```cs
using System;
using Fullscreen.RuntimeStudio.Runtime.UI.ComponentDrawers;

public sealed class HealthDrawer :
    IRuntimeComponentDrawer
{
    public Type ComponentType => typeof(MyHealth);

    public void Draw(
        ComponentDrawerContext context,
        Component component)
    {
        var health = component as MyHealth;
        if (health == null) return;

        context.DrawFloat(
            "Health",
            health.Current,
            value => health.Current = value
        );

        context.DrawBool(
            "Invincible",
            health.Invincible,
            value => health.Invincible = value
        );
    }
}
```

Register it:

```cs
builder.AddDefaultComponentDrawer(
    new HealthDrawer()
);
```

#### Context Helpers

```cs
context.DrawBool(...)
context.DrawInt(...)
context.DrawFloat(...)
context.DrawVector2(...)
context.DrawVector3(...)
context.DrawText(...)
```

***

## Property Drawers

Property drawers customize how nested objects are displayed.

```cs
using Fullscreen.RuntimeStudio.Runtime.UI.PropertyDrawers;

public sealed class StatBlockDrawer :
    IRuntimePropertyDrawer
{
    public bool CanDraw(
        PropertyDrawerContext context)
    {
        return context.DeclaredType == typeof(MyStatBlock);
    }

    public void Draw(
        PropertyDrawerContext context)
    {
        var value = context.Value as MyStatBlock;

        context.FieldDrawer.DrawValue(
            context.Parent,
            "Power",
            typeof(float),
            value.Power,
            $"{context.Path}.power",
            next => value.Power = (float)next
        );
    }
}
```

Register it:

```cs
builder.AddDefaultPropertyDrawer(    new StatBlockDrawer());
```

## Hierarchy Adornments

Hierarchy adornments display small icons next to objects in the hierarchy.

{% code expandable="true" %}

```cs
builder.AddDefaultHierarchyAdornment(
    new HierarchyAdornmentDefinition
    {
        Id = "my-game.spawn",
        Tooltip = "Spawn Point",
        IsVisible = go =>
            go.GetComponent<MySpawnPoint>() != null
    }
);
```

{% endcode %}

Use them to highlight important objects or runtime data.

## Hierarchy Context Actions

Context actions appear when right-clicking objects in the hierarchy.

{% code expandable="true" %}

```cs
public sealed class CreateSpawnAction :
    IRuntimeHierarchyContextAction
{
    public int Order => 100;

    public bool Supports(
        HierarchyActionContext context)
    {
        return true;
    }

    public bool IsEnabled(
        HierarchyActionContext context)
    {
        return true;
    }

    public string GetMenuPath(
        HierarchyActionContext context)
    {
        return "Create/Spawn Point";
    }

    public void Execute(
        HierarchyActionContext context)
    {
        new GameObject("Spawn Point");
    }
}
```

{% endcode %}

Register it:

{% code expandable="true" %}

```cs
builder.AddDefaultHierarchyAction(
    new CreateSpawnAction()
);
```

{% endcode %}

## FieldInspector

`FieldInspector` provides the same field UI used by Runtime Studio's built-in inspector.

Draw a value:

```cs
context.FieldDrawer.DrawValue(
    context.Parent,
    "Speed",
    typeof(float),
    speed,
    "speed",
    value => speed = (float)value
);
```

Draw object fields:

```cs
context.FieldDrawer.DrawObjectFields(
    context.Parent,
    settings,
    "settings",
    0
);
```

Draw a type picker:

```css
context.FieldDrawer.DrawTypeSelector(
    context.Parent,
    "action",
    typeof(MyAction),
    currentType,
    selected => Create(selected),
    "Select Action"
);
```

## EditorStyles

Use `EditorStyles` when creating custom UI so your extension matches the rest of Runtime Studio.

#### Titles

```cs
parent.Add(
    EditorStyles.Title("Settings")
);
```

#### Buttons

```cs
parent.Add(    
    EditorStyles.Button(
          "Reset",    
           ResetSettings    
           )
);
```

#### Rows

```cs
var row = EditorStyles.RowElement();
```

#### Panels

```cs
var panel =    EditorStyles.PanelElement("MyPanel");
```
