Skip to content

Instantly share code, notes, and snippets.

@karl-
Created February 11, 2021 16:47
Show Gist options
  • Save karl-/1956b1dcd8d76fd849c3315a04777c31 to your computer and use it in GitHub Desktop.
Save karl-/1956b1dcd8d76fd849c3315a04777c31 to your computer and use it in GitHub Desktop.
marp theme class
true
default

Overlays API


Old Way


Old Way

class MyProjectScript : EditorTool
{
    public override void OnToolGUI()
    {
        // ... ?
    }
}

Old Way

class MyProjectScript : EditorTool
{
    OverlayWindow m_Overlay;

    void OnEnable()
    [
        m_Overlay = new OverlayWindow(
                new GUIContent("My Overlay"),
                OverlayGUI,
                int.MaxValue,
                null,
                SceneViewOverlay.WindowDisplayOption.MultipleWindowsPerTarget);
    }

    public override void OnToolGUI()
    {
        SceneViewOverlay.ShowWindow(m_Overlay);
    }

    void OverlayGUI(Object target, SceneView sceneView)
    {
        GUILayout.Label("hello!");
    }
}

Old Way

height:600px


Old Way

  • IMGUI only
  • Anchored to bottom right of screen
  • Not resizable
  • Internal API
  • No consistency for third party tools

New API

  • Class implementing Overlay
  • Tagged with OverlayAttribute

New API

public abstract class Overlay
{
    // Less interesting properties omitted
    public UnityEditor.EditorWindow containerWindow { get; }
    public bool displayed { get; set; }
    public string displayName { get; set; }
    public bool floating { get; }
    public UnityEngine.Vector2 floatingPosition { get; set; }
    public string id { get; }
    public abstract VisualElement CreatePanelContent();

    protected virtual void OnCollapsed();
    public virtual void OnDestroy();
    protected virtual void OnExpanded();
    public virtual void OnInitialize();
    public virtual void OnPopupWindowDestroy(EditorWindow window);
    public virtual void OnPopupWindowInitialize(EditorWindow window);
    public void Undock();
}

New API

public sealed class OverlayAttribute : System.Attribute
{
    public string displayName { get; }
    public System.Type editorWindowType { get; }
    public string id { get; }

    public OverlayAttribute(Type editorWindowType, string displayName, bool defaultLayout = False) {}

    public OverlayAttribute(System.Type editorWindowType, string id, string displayName, bool defaultLayout = False) {}

    public OverlayAttribute(System.Type editorWindowType, string id, string displayName, string ussName, bool defaultLayout = False) {}
}

Simple Case

using UnityEditor;
using UnityEditor.Overlays;
using UnityEngine.UIElements;

[Overlay(typeof(SceneView), "Selection Count")]
[Icon("MyPackage/Icons/SelectionCount.png")]
public class SelectionCount : Overlay
{
    Label m_Label;

    public override void OnInitialize() => Selection.selectionChanged += UpdateLabel;

    public override void OnDestroy() => Selection.selectionChanged -= UpdateLabel;

    public override VisualElement CreatePanelContent()
    {
        return m_Label = new Label($"Selection Contains {Selection.count} objects(s)");
    }

    void UpdateLabel()
    {
        if (m_Label != null)
            m_Label.text = $"Selection contains {Selection.count} objects(s)";
    }
}

Simple Case


Simple Case Break Down

[Overlay(typeof(SceneView), "Selection Count")]
[Icon("MyPackage/Icons/SelectionCount.png")] // <-Optional
  • Register Overlay as applicable to Scene View, with display name.
  • IconAttribute is not specific to Overlay. It is a simple way to define an icon for a MonoBehaviour that respects the built-in naming conventions (ex, "d_" and "@2x")
    • Not required
    • If no Icon is found the Overlay will create one from first two significant letters.

Simple Case Break Down

public class SelectionCount : Overlay
  • All Overlays inherit Overlay class.

Simple Case Break Down

    Label m_Label;

    public override void OnInitialize() => Selection.selectionChanged += UpdateLabel;

    public override void OnDestroy() => Selection.selectionChanged -= UpdateLabel;
  • Use OnInitialize and OnDestroy to manage lifecycle resources.

Simple Case Break Down

    public override VisualElement CreatePanelContent()
    {
        return m_Label = new Label($"Selection Contains {Selection.count} objects(s)");
    }
  • CreatePanelContent expects that a new VisualElement is returned each call.
  • This is a different pattern than appending to rootVisualElement directly, as with EditorWindow for example.

Simple Case Break Down

    void UpdateLabel()
    {
        if (m_Label != null)
            m_Label.text = $"Selection contains {Selection.count} objects(s)";
    }
}
  • Manage your UI Elements content as usual

What About Porting Existing Overlays?

  • We've already refactored all existing overlays in trunk
  • IMGUIContainer works out of the box
  • ...but preferably just spend a few moments to rewrite using UI Elements

Toolbars

  • A reusable VisualElement with EditorToolbarElement attribute (ex)
    • EditorToolbarToggle
    • EditorToolbarDropdown
    • EditorToolbarButton
  • An Overlay implementing the ToolbarOverlay type
    • Specialized Overlay to provide content for horizontal and vertical toolbars

Toolbars

    [EditorToolbarElement("SceneView/Lighting", typeof(SceneView))]
    sealed class SceneLightingElement : EditorToolbarToggle, IAccessContainerWindow
    {
        public object containerWindow { get; set; }
        SceneView sceneView => context as SceneView;

        public SceneLightingElement()
        {
            name = "SceneviewLighting";
            RegisterValueChangedCallback(evt => sceneView.sceneLighting = evt.newValue);
        }
    }

Toolbars

[Overlay(typeof(SceneView), k_Id,"SceneView Settings",true)]
class SceneViewToolBar : ToolbarOverlay
{
    const string k_Id = "unity-scene-view-toolbar";
    protected override void PopulateToolbar(EditorToolbar toolbar)
    {
        toolbar.AddElement("SceneView/Camera Mode");
        toolbar.AddElement("SceneView/2D");
        toolbar.AddElement("SceneView/Lighting");
        // ...
    }
}

Toolbars


Toolbars


Toolbars


Toolbars (EditorToolbarElement)

    [EditorToolbarElement("SceneView/Lighting", typeof(SceneView))]
  • EditorToolbarElement is the attribute used to identify pieces of a Toolbar.

Toolbars (EditorToolbarElement)

    sealed class SceneLightingElement : EditorToolbarToggle, IAccessContainerWindow
  • Just a VisualElement that is styled correctly for a toolbar
  • We provide out of the box a set of default elements (EditorToolbar{Button, Toggle, Dropdown, ...})
  • Can author custom VisualElement as well
  • IAccessContainerWindow provides access to the Overlay.containerWindow

Toolbars (EditorToolbarElement)

public SceneLightingElement()
{
    name = "SceneviewLighting";
    tooltip = L10n.Tr("When toggled on, the Scene lighting is used. When toggled off, a light attached to the Scene view camera is used.");
    RegisterCallback<AttachToPanelEvent>(OnAttachedToPanel);
    RegisterCallback<DetachFromPanelEvent>(OnDetachFromPanel);
    this.RegisterValueChangedCallback(evt => sceneView.sceneLighting = evt.newValue);
    SceneViewToolbarElements.AddStyleSheets(this);
}
// ... implementation
  • Otherwise this is authored exactly like a typical VisualElement

Toolbars (ToolbarOverlay)

[Overlay(typeof(SceneView), k_Id,"SceneView toolbar",true)]
  • OverlayAttribute is unchanged (optional id and defaultLayout params shown here)

Toolbars (ToolbarOverlay)

    protected override void PopulateToolbar(EditorToolbar toolbar)
    {
        toolbar.AddElement("SceneView/Camera Mode");
        // ...
    }
  • This is the only function a ToolbarOverlay needs to implement
  • Add existing EditorToolbarElement items identified by the EditorToolbarElement.id property

Toolbars

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment