Overlays Developer Guide

How to Build Your Own Overlays

Hello! Here’s a quick-start guide to building your own Overlays with the new system. Please post questions and discussions here, we’ll be happy to help out.

For general Overlays info, and to download the public build, view the Overlays Public Preview thread.

Please keep in mind, this is an early preview, subject to change.

The Simplest Way

1. Create a class that inherits from Overlay class

{
}```

**2.** Add the Overlay attribute and provide:

- Which type of window it can appear in, use **EditorWindow** type if you want it to appear in any window that supports overlays
- a unique id
- A name, this will be displayed in its header and menu

```[Overlay(typeof(SceneView), k_Id, "My Awesome Overlay")]
class MyOverlay : Overlay
{
const string k_Id = "my-custom-overlay";
}```

**3.** Implement the **CreatePanelContent** method and have it return the content you wish to be displayed in the overlay. From this point refer to UI Toolkit docs to know how to build UI.

```public override VisualElement CreatePanelContent()
{
var myContent = new VisualElement { name = "my-content" };
return myContent;
}```

**4.** Add the icon attribute to specify which icon to use when collapsed, if no attribute is specified an icon with the 2 most significant letters of the overlay name will be generated for you

**Important Note: IconAttribute** is not available in the current preview build, but will be public when released.

```[Overlay(typeof(SceneView), k_Id, "My Awesome Overlay")]
[Icon("path/to/myIcon.png")]
class MyOverlay : Overlay
{
const string k_Id = "my-custom-overlay";
}```

**Note:** If your overlay does not appear, be aware that is the default behavior. Bring up the overlay menu by pressing the space bar in the Scene View (you can change this in the shortcut manager) you should see your overlay appear in the menu.

**Lifecycle**

Overlay visibility can be controlled through Overlay Menu, the 3 dot menu in the corner of the window or through code by directly changing the value of the **displayed** property. You may want to prevent the users from controlling the visibility and control it yourself ( from a toggle in a toolbar for example ) in this case be sure to set the property **userControlledVisibility** to false, in the Overlay constructor for example.

**Toolbars**

If implemented this way, you will notice that your overlay will collapse when inserted into a toolbar, you can change this behavior by implementing the **ICreateHorizontalToolbar** and the **ICreateVerticalToolbar** interfaces.

```[Overlay(typeof(SceneView), k_Id, "My Awesome Overlay")]
class MyOverlay : Overlay, ICreateHorizontalToolbar, ICreateVerticalToolbar
{
const string k_Id = "my-custom-overlay";
public override VisualElement CreatePanelContent()
{
var myContent = new VisualElement { name = "my-content" };
return myContent;
}
public VisualElement CreateHorizontalToolbarContent()
{
var myContent = new VisualElement { name = "my-horizontal-content" };
return myContent;
}
public VisualElement CreateVerticalToolbarContent()
{
var myContent = new VisualElement { name = "my-vertical-content" };
return myContent;
}
}```

**IMGUI**

If you are converting some existing UI code to use the overlay system, chances are it is written in IMGUI. We strongly advise to take this opportunity to convert your whole UI to UITK. In the case where this is not possible you can use the **IMGUIOverlay** class.

```[Overlay(typeof(SceneView),k_OverlayID,k_DisplayName)]
internal class MyIMGUIOverlay : IMGUIOverlay
{
public const string k_OverlayID = "my-custom-overlay";
const string k_DisplayName = "My Awesome Overlay";
public override void OnGUI()
{
//Draw IMGUI stuff here
}
}```

**Other Windows**

The overlay system is not restricted to the SceneView, any **EditorWindow** can opt in to have it enabled. All it needs to do is implement the **ISupportsOverlays** interface. Overlays targeting **EditorWindow** will automatically be available, otherwise Overlays need to target your **EditorWindow** type.
10 Likes

That’s awesome, waiting for the weekend to try this out. One question, if I may. That [Icon] Attribute, does it necessarily need a string path?

Will there be a mechanism to inherit from the IconAttribute class and implement your own custom icon? Somthing like this:

public MyCustomIconAttribute : IconAttribute
{
    public override Texture iconTexture => GenerateMyCustomTexture();

    private Texture GenerateMyCustomTexture()
    {
        // custom code that generates a Texture, or loads it from somewhere.
    }
}

And use it like this

[Overlay(typeof(SceneView), k_Id, "My Awesome Overlay")]
[MyCustomIcon]
class MyOverlay : Overlay
{
   const string k_Id = "my-custom-overlay";
}

The reason being that you don’t always want to hard-code textures or maybe you want to generate them on the fly (based on whether Unity dark skin is on or off), among other reasons.

Anyway, I’m super excited about this new Overlays feature!

The icon loading follows the same path that built-in icons do. It will search for icons matching a naming scheme to best match the current editor theme and screen resolution. Ex, you can provide MyIcon.png d_MyIcon.png MyIcon@2x.png d_MyIcon@2x.png (@2x being resolution, with the “d_” prefix to specific professional theme).

2 Likes

Hi @Catsoft-Works , @kaarrrllll beat me to it. At this point yes it does need a string path and unfortunately in this build the IconAttribute is internal, we will make sure to make it public for release. Interested to know what your other use cases would be for generating icons.

1 Like

Sure! I guess it’s a more personal reason, so I understand that’s not something that would be widely used. But as an Asset Store developer, I try to have the least amount of textures inside each scripting package. The reason being that I don’t want to pollute the user’s project with textures he/she shouldn’t be using (gizmos, Inspector icons, …). Otherwise, when they click on any Texture field picker, they get hundreds of textures non-related to their game project.

For this reason, I’m moving all icons to a hardcoded byte[ ] array that is reconstructed whenever it’s needed using the Texture2D.LoadRawTextureData(…) method. This has the advantage of not getting in the way of the user’s texture organisation or naming convention.

Hope that makes sense, though as I said, as far as I know only Bolt and my (future) packages do it like this. I am more than happy to provide feedback if you have any questions to discuss :slight_smile:

6 Likes

That’s a very cool idea! How is it working out? Similar problems for us/others, of course. If this is a good solution, I can definitely see us wanting to ensure Overlays can work with it.

2 Likes

Thanks! So far it’s working fantastic! I’ve been using this for a couple of months and there aren’t any drawbacks so far. I’ve attached a screenshot showing how it looks. I wouldn’t want to hijack this thread’s topic :stuck_out_tongue: So before doing so, let me know if you want me to give the implementation details here or somewhere else.

6608836--752008--Screenshot 2020-12-10 at 18.46.28.png

2 Likes

@Catsoft-Works - sure, a separate thread would be great, thanks :slight_smile: I’m hearing from others here that other solutions might be better, maybe they/we can discuss in that thread :slight_smile:

2 Likes

Sure! I’ve created this thread: https://discussions.unity.com/t/820167 Cheers everyone!

6 Likes

@gabrielw_unity Can we use the new tooling system in a production project? We are 2021.1 Thanks, Marek.

1 Like

We’ve been playing witht he Overlay system and it’s awesome. It takes some time to get used to it and the contrast between toggles when they are on/off it’s a bit too subtle, but overall it’s great and a big step forward. More so knowing that can (or will? haven’t tried this yet) be used in other EditorWindows.

One thing we haven’t been able to do is to use the BuiltinToolsStrip visual element, along with the ToolButton, to create a chain of buttons that mimics the ones made by Unity.

@gabrielw_unity - I guess we can create our own stylizing, but I’m wondering whether these will be exposed soon, so we can leverage them in our own tools?

1 Like

2021.2 is where the visuals landed, but the foundation is all there for 2021.1. I’d say upgrade if you can! :smile:

Hey! :slight_smile: I would assume that’s either on the way, or something else to solve that problem. I’ll check!

1 Like

Quick question, is that the Icon attribute only looks for the path in the “Editor Default Resources” folder?

How should we go about in a situation that the icon is located in the Packages folder instead of the Assets folder?

[Overlay(typeof(SceneView), k_OverlayID, k_DisplayName)]
[Icon("proarray-icon.png")]
internal class ProArrayOverlay : IMGUIOverlay
{
...
}

7157824--856930--upload_2021-5-20_11-46-31.png

It looks from the project root. So if you have a texture inside the Assets folder called “MyTexture.png” you use

[Icon("Assets/MyTexture.png")]

If your texture is inside the Packages folder, use the “Packages/…” prefix

2 Likes

Thanks! Oh, no wonder, just also figured out the path for individual packages is with the package name instead of the actual folder name. It is working!

[Overlay(typeof(SceneView), k_OverlayID, k_DisplayName)]
[Icon("Packages/com.bennykok.proarray/Editor/Icons/proarray-icon.png")]
internal class ProArrayOverlay : IMGUIOverlay
{
    ...
}

Now I’m wondering how to properly handle the icon if the package can be installed both in the Assets folder and Packages folder, after looking at @Catsoft-Works 's previous reply about extending the IconAttribute, I end up doing something like this, and seems like its working.

[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
[Conditional("UNITY_EDITOR")]
public class ProArrayIconAttribute : IconAttribute
{
    public ProArrayIconAttribute() : base(GetRelativeIconPath()) {}
    public static string GetRelativeIconPath(string asmdef = "BennyKok.ProArray.Editor")
    {
        var p = Path.GetDirectoryName(CompilationPipeline.GetAssemblyDefinitionFilePathFromAssemblyName(asmdef));
        return p + "/Icons/proarray-icon.png";
    }
}

[Overlay(typeof(SceneView), k_OverlayID, k_DisplayName)]
[ProArrayIcon]
internal class ProArrayOverlay : IMGUIOverlay
{
...
}
2 Likes

there doesn’t seem to be a userControlledVilsibility property as mentioned in the original post. if that property won’t be available, another potential solution to my issue would be only allowing my overlay in my custom PreviewSceneStage somehow (implementing ISupportsOverlays and using it as the editor window type in the overlay attribute does not work).

having purpose-built overlays for custom PreviewSceneStage classes seems like a perfect match. it would be a powerful feature to add if it isn’t already possible somehow.

@gabrielw_unity the new overlays seem to break an “old” way of drawing windows in the scene view (using GUILayout.Window) that I’ve been using in my tools. Is this on purpose?

To reproduce this, put the following script in an editor folder in Unity 2021.2(b13). Then click on “Window → Start Test”. Notice how no window is shown in the scene view. In the console, “GUILayout.Window() called” is shown and “Callback called” is not shown. Then execute the same script in Unity 2021.1 and notice how the window is drawn correctly.

using UnityEngine;
using UnityEditor;

public class Test
{
    [MenuItem("Window/Start Test")]
    public static void MenuItemClicked()
    {
        SceneView.duringSceneGui += OnSceneGUI;
    }

    private static void OnSceneGUI(SceneView sceneView)
    {
        Handles.BeginGUI();

        Debug.Log("GUILayout.Window() called");
        GUILayout.Window(2, new Rect(sceneView.position.width - 110, sceneView.position.height - 130, 100, 100), WindowCallback, "Title");

        Handles.EndGUI();
    }

    private static void WindowCallback(int id)
    {
        Debug.Log("Callback called"); // <-- not called in Unity 2021.2b13
        GUILayout.Button("A Button");
    }
}

Is this behavior on purpose or is this a bug?

Thanks.

This was a regression, there is a PR in flight to correct it.

1 Like

Ok great, thanks for looking into this. Btw. this is the related bug report ticket (guess you can eventually close this):1369148

1 Like