# Custom Editor Windows - Part 1

{% hint style="danger" %}
The documentation has moved to: <https://mitschmr-studios.io/documentation/api-guides/customeditorwindowspart1.html>

This version will no longer be updated and maintained.&#x20;
{% endhint %}

This topic is split into two parts. In the first part, I want to introduce you to how to build your own Unity editor windows. Part two shows you how to make custom Inspector GUIs.&#x20;

### Prerequisites

* Install Unity
* Install IDE (I use Visual Studio 2019)
* Import Game Creator (**optional**, only if needed for the window)
* Import and enable modules (**optional**, only if needed for the window)

### Unity Editor Windows

You all know the Scene window, the Inspector and many more windows in Unity. But what is actually an Editor window?&#x20;

An Editor window is a window in Unity that shows certain content. According to Unity, this includes three simple steps:&#x20;

* Create a script that derives from EditorWindow
* Use code to trigger the window to display itself
* Implement the GUI code for your tool

I have already written some information on Editor Windows in [Inventory Custom Entries](https://docs.mitschmr-studios.io/gc-api-guides/inventory-custom-entries#unity-editor-knowledge):&#x20;

> Normally, when a script derives from `Editor` and you want to start building your project, the build will fail. This is because the class `Editor` is not available in built versions of the game, only in the Unity Editor. We have to place the script in a folder with the name `Editor`, so it doesn't get included in the built game. But why the name `Editor`? Unity uses so called **Special folder names** to tread a folder's content in a special way. See [Unity Special Folders](https://docs.unity3d.com/Manual/SpecialFolders.html) for more information.&#x20;

The same applies to the class `EditorWindow`. But it is not as easy as Unity tells you to build these windows. But let's start from the beginning.&#x20;

### Your first window

You should now know that we need a folder called **Editor** for displaying editor window. Add this folder to your scripts folder and create a new C# script. I will name my script **TutorialWindowEditor.cs**. Let it derive from `EditorWindow`, add the using statement for `UnityEditor` and delete the Start and Update methods. Your script should now look like this:&#x20;

```csharp
using UnityEditor;

public class TutorialWindowEditor : EditorWindow
{
    
}
```

#### Showing the window

In order to show a window, Unity needs a menu item which calls the `ShowWindow()` method. With a menu item, you are able to open a window from anywhere in top bar of the Unity Editor. The default behavior is to show already opened windows of this type. Let's do this:&#x20;

```csharp
[MenuItem("Window/Tutorial Window")] // Adds a menu entry under Window

public static void ShowWindow()
{
    EditorWindow.GetWindow(typeof(TutorialWindowEditor), false, "Tutorial Window");
    
    // Because we derive from EditorWindow, "EditorWindow." is not necessary, 
    // below code works too.
    // GetWindow(typeof(TutorialWindowEditor));
}
```

![](https://3625548948-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M6E9D__lMnHqqxqKxIn%2F-MDVRRrPH23o5bIojyqk%2F-MDVRdRcIKYffL-E1evX%2Fimage.png?alt=media\&token=41fd4aa4-26ec-413d-85f2-6d0c0e2fa51b)

Add your class name to `typeof(<your class name>)`. Otherwise it won't work, because Unity most likely won't find this type (class). We also added a title to the window, so it doesn't show the class name. When you switch back to Unity and let it compile, you can open your window from the path you entered in the menu item. If you click again on the button, it sets the focus on the window because there is already one opened.&#x20;

#### Adding UI elements

Editor windows use the method `OnGUI()` to render the content of a window. This is where the actual code is written. There are many different elements you can add, both in [**EditorGUI**](https://docs.unity3d.com/ScriptReference/EditorGUI.html) and [**EditorGUILayout**](https://docs.unity3d.com/ScriptReference/EditorGUILayout.html). The difference between them is that **EditorGUILayout** is auto laid out, which means it adjusts automatically to the window size.&#x20;

Sometimes you also have to use [GUI](https://docs.unity3d.com/ScriptReference/GUI.html) and [GUILayout](https://docs.unity3d.com/ScriptReference/GUILayout.html). The difference is again the auto layout. The difference between the Editor and non-Editor styles is that the Editor styles were introduced later. Let's start with a field for text and color and add a titel to each of it:&#x20;

```csharp
void OnGUI()
{
    EditorGUILayout.LabelField("Color");
    EditorGUILayout.ColorField(new Color(1,1,1));

    EditorGUILayout.LabelField("Text");
    EditorGUILayout.TextField("Change this text");
}
```

Result:&#x20;

![](https://3625548948-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M6E9D__lMnHqqxqKxIn%2F-MDVT6XKal00eEm9lKiB%2F-MDVUSPfzA8Mx4wFjKv5%2Fimage.png?alt=media\&token=562f85c5-35c5-401d-9c87-64d3b5926429)

This looks ok for now, but it doesn't save the values, they get overwritten all the time. Let's change this behavior. First, add a private color and text variable before the menu item, set the GUI elements to display the values and change the values accordingly to the input.&#x20;

```csharp
private Color color = new Color(1, 1, 1); // You can set any color you want
private string text = "Change this text"; // You can set any text you want

<...>

void OnGUI() 
{
    EditorGUILayout.LabelField("Color");             // Title
    color = EditorGUILayout.ColorField(color);       // Color field

    EditorGUILayout.LabelField("Text");              // Title
    text = EditorGUILayout.TextField(text);          // Text field
}
```

When we now change the values, they are saved until the window gets closed.&#x20;

![](https://3625548948-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M6E9D__lMnHqqxqKxIn%2F-MDVWtesf_y9r9nf7qZ_%2F-MDVWxC2mRbcwyynyGmi%2Fimage.png?alt=media\&token=1b7b7b00-78cd-4065-a4a3-fb2c85642820)

#### Using the correct method

Editor windows contain similar methods like gameobjects. Visit [EditorWindow](https://docs.unity3d.com/ScriptReference/EditorWindow.html) for all methods and more. Here is a list of the (in my opinion) most important methods with the description and their use cases:&#x20;

| Name        | Description                          | Use cases                                      |
| ----------- | ------------------------------------ | ---------------------------------------------- |
| Awake()     | Called as a new window is opened     | Execute code when window is opened             |
| Close()     | Closes the window                    | Close window                                   |
| GetWindow() | Gets a window of a certain type      | Show already opened window / create new window |
| OnDisable() | Called when window goes out of scope | Clear / save variables                         |
| OnEnable()  | Called when object is loaded         | Load variables / data                          |
| OnGUI()     | Render your window code              | Window design                                  |
| Repaint()   | Repaints the window                  | Update window if content has changed           |

**Example:** Reading data from disk should **NOT** happen in OnGUI, because OnGUI gets called up to several times per frame. Do this in i.e. OnEnable(), which gets only called when the window is loaded.&#x20;

### More advanced examples

You still need the basic class, menu item, ShowWindow() etc.

#### Toolbar

```csharp
private static readonly GUIContent[] TAB_NAMES = new GUIContent[]
{
    new GUIContent("Tab 1"),
    new GUIContent("Tab 2"),
    new GUIContent("Tab 3"),
    new GUIContent("Tab 4")
};

private int tabIndex = 0;

void OnGUI()
{
    tabIndex = GUILayout.Toolbar(tabIndex, TAB_NAMES);

    switch (tabIndex)
    {
        case 0: DoSomething1(); break;
        case 1: DoSomething2(); break;
        case 2: DoSomething3(); break;
        case 3: DoSomething4(); break;
    }
}

// Void DoSomething1(), DoSomething2()...
```

![](https://3625548948-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M6E9D__lMnHqqxqKxIn%2F-MDVtHh_AxY3SfzIjVXE%2F-MDWHDT4od5z5PT8hn9R%2Fimage.png?alt=media\&token=fb4d4eaf-5974-4c26-aa17-24845444a23b)

#### Scrollarea with custom font and dummy text

```csharp
private Vector2 _scrollPosition;
private GUIStyle _tutorialFont = new GUIStyle();

void OnEnable()
{
    _tutorialFont.fontSize = 13;
    _tutorialFont.wordWrap = true;
    _tutorialFont.margin = new RectOffset(5, 5, 0, 0);
}

void OnGUI()
{
    _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);

    // Split for better readability
    EditorGUILayout.LabelField("Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, " +
        "sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. " +
        "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. " +
        "At vero eos et accusam et justo duo dolores et ea rebum. " +
        "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
        , _tutorialFont);

    EditorGUILayout.EndScrollView();
}
```

![](https://3625548948-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M6E9D__lMnHqqxqKxIn%2F-MDWKA_R9Kb2Cwdv0eXb%2F-MDWKLY3f9ZQ__-6ebHF%2Fimage.png?alt=media\&token=994e05d8-5d47-481f-a90c-2d2f873c6a72)

#### Custom Object fields

```csharp
private Animation _animObject;
private Actions _actionObject;
private ScriptableObject _scriptableObject;

void OnGUI()
{
    _animObject = (Animation)EditorGUILayout.ObjectField(_animObject, typeof(Animation), false);
    _actionObject = (Actions)EditorGUILayout.ObjectField(_actionObject, typeof(Actions), true);
    _scriptableObject = (ScriptableObject)EditorGUILayout.ObjectField(_scriptableObject, typeof(ScriptableObject), false);
}
```

![](https://3625548948-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M6E9D__lMnHqqxqKxIn%2F-MDWPRgldTNGwhI5x_Ix%2F-MDWPUwNA-O61bkqgDZA%2Fimage.png?alt=media\&token=70a6930c-19f0-44cd-b71e-b147cfb4c7d7)
