Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Show a flyout project window in the scene view
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.Compilation;
using UnityEngine;
public static class FlyoutProjectWindow
{
private const double _animDuration = 0.25f;
private const float _sizeRatio = 0.4f;
private static Func<Event, bool> _triggerEvent = e =>
{
#if UNITY_EDITOR_WIN
return e.alt && e.keyCode == KeyCode.P;
#else
return e.control && e.shift && e.keyCode == KeyCode.P;
#endif
};
private static Func<Event, SceneView, bool> _clickSceneView = (e, s) =>
{
return e.type == EventType.MouseUp && e.button == 0 && s.position.Contains(e.mousePosition);
};
private static Type _projectBrowserType = System.Reflection.Assembly.GetAssembly(typeof(EditorWindow)).GetTypes().First(t => t.Name == "ProjectBrowser");
private class WindowData
{
public EditorWindow Browser;
public bool Showing;
public bool EventActive;
public double AnimStartTime;
public Rect Target;
public Rect StartSize;
}
[InitializeOnLoadMethod]
private static void Init()
{
SceneView.duringSceneGui += OnSceneGUI;
CompilationPipeline.assemblyCompilationStarted += CloseAllWindows;
}
[DidReloadScripts]
private static void ScriptsCompiling()
{
CloseAllWindows(null);
}
private static void CloseAllWindows(string _)
{
foreach (var window in _windows)
if (window.Value.Browser != null)
window.Value.Browser.Close();
_windows.Clear();
}
private static Dictionary<SceneView, WindowData> _windows = new Dictionary<SceneView, WindowData>();
private static void UpdateBrowserPosition(SceneView sceneView, WindowData data)
{
if (data.Browser == null)
{
_windows.Remove(sceneView);
return;
}
var lerp = (float)((EditorApplication.timeSinceStartup - data.AnimStartTime) / _animDuration);
if (lerp <= 0f) return;
if (lerp >= 1f)
{
if (!data.Showing || sceneView == null)
{
if (data.Browser != null) data.Browser.Close();
_windows.Remove(sceneView);
return;
}
else
{
var scene = sceneView.position;
scene.y += scene.height * (1f - _sizeRatio);
scene.height *= _sizeRatio;
data.Target = data.StartSize = scene;
data.Browser.position = scene;
data.Browser.Repaint();
data.EventActive = false;
}
return;
}
data.Browser.Focus();
var time = (float)(EditorApplication.timeSinceStartup - data.AnimStartTime);
var lerpValue = OutBackEase(time, (float)_animDuration);
data.Browser.position = new Rect(data.Target.x, Mathf.Lerp(data.StartSize.y, data.Target.y, lerpValue), data.Target.width, Mathf.Lerp(data.StartSize.height, data.Target.height, lerpValue));
data.Browser.Repaint();
SceneView.RepaintAll();
}
private static float OutBackEase(float time, float duration, float overshoot = 0.25f)
{
return ((time = time / duration - 1) * time * ((overshoot + 1) * time + overshoot) + 1);
}
private static bool ValidateBrowser(SceneView sceneView, EditorWindow browser)
{
if (sceneView == null)
{
_windows.Remove(sceneView);
if (browser != null)
{
browser.Close();
}
return false;
}
if (!_windows.ContainsKey(sceneView) && browser != null)
{
browser.Close();
return false;
}
if (browser == null)
return false;
return true;
}
private static void OnSceneGUI(SceneView sceneView)
{
var e = Event.current;
WindowData data = null;
if (_windows.TryGetValue(sceneView, out data))
{
UpdateBrowserPosition(sceneView, data);
}
if (e == null) return;
if (_triggerEvent(e) || ((data?.Showing ?? false) && _clickSceneView(e, sceneView)))
{
if (data?.EventActive ?? false) return;
data = GetProjectBrowser(sceneView);
data.EventActive = true;
data.AnimStartTime = EditorApplication.timeSinceStartup;
data.Showing = !data.Showing;
data.StartSize = data.Target;
data.Target = sceneView.position;
if (data.Showing)
{
data.Target.y += data.Target.height * (1f - _sizeRatio);
data.Target.height *= _sizeRatio;
}
else
{
data.Target.y += data.Target.height;
data.Target.height = 1;
}
e.Use();
}
}
private static WindowData GetProjectBrowser(SceneView sceneView)
{
try
{
if (!_windows.TryGetValue(sceneView, out var data) || data.Browser == null)
{
data = new WindowData();
data.Browser = ScriptableObject.CreateInstance(_projectBrowserType) as EditorWindow;
var scenePosition = sceneView.position;
scenePosition.y += scenePosition.height;
scenePosition.height = 1f;
data.Target = scenePosition;
data.Browser.ShowPopup();
data.Browser.position = data.Target;
data.Browser.Repaint();
_windows.Remove(sceneView);
_windows.Add(sceneView, data);
Action<SceneView> validate = null;
validate = _ =>
{
if (!ValidateBrowser(sceneView, data.Browser))
SceneView.beforeSceneGui -= validate;
};
SceneView.beforeSceneGui += validate;
EditorApplication.CallbackFunction validateEditor = null;
validateEditor = () =>
{
if (!ValidateBrowser(sceneView, data.Browser))
EditorApplication.update -= validateEditor;
};
EditorApplication.update += validateEditor;
}
return data;
}
catch (Exception ex)
{
Debug.LogException(ex);
}
return null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment