2025-06-08 00:39:11 +09:00

1820 lines
59 KiB
C#

#if UNITY_EDITOR
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.ShortcutManagement;
using System.Reflection;
using System.Linq;
using UnityEngine.UIElements;
using UnityEngine.SceneManagement;
using UnityEditor.SceneManagement;
using System.Diagnostics;
using Type = System.Type;
using Delegate = System.Delegate;
using Action = System.Action;
using static VTabs.Libs.VUtils;
using static VTabs.Libs.VGUI;
namespace VTabs
{
public static class VTabs
{
static void Update()
{
var lastEvent = typeof(Event).GetFieldValue<Event>("s_Current");
void scrollInteractions()
{
if (isKeyPressed) { sidesscrollPosition = 0; return; }
if (lastEvent.delta == Vector2.zero) return;
if (lastEvent.type == EventType.MouseMove) { sidesscrollPosition = 0; return; }
if (lastEvent.type == EventType.MouseDrag) { sidesscrollPosition = 0; return; }
if (lastEvent.type != EventType.ScrollWheel && delayedMousePosition_screenSpace != EditorGUIUtility.GUIToScreenPoint(lastEvent.mousePosition)) return; // uncaptured mouse move/drag check
if (lastEvent.type != EventType.ScrollWheel && Application.platform == RuntimePlatform.OSXEditor && lastEvent.delta.x == (int)lastEvent.delta.x) return; // osx uncaptured mouse move/drag in sceneview ang gameview workaround
void switchTab(int dir)
{
if (!VTabsMenu.switchTabsEnabled) return;
if (!(EditorWindow.mouseOverWindow is EditorWindow hoveredWindow)) return;
if (!hoveredWindow.docked) return;
if (hoveredWindow.maximized) return;
var tabs = GetTabList(hoveredWindow);
var i0 = tabs.IndexOf(hoveredWindow);
var i1 = Mathf.Clamp(i0 + dir, 0, tabs.Count - 1);
tabs[i1].Focus();
UpdateTitle(tabs[i1]);
}
void moveTab(int dir)
{
if (!VTabsMenu.moveTabsEnabled) return;
if (!(EditorWindow.mouseOverWindow is EditorWindow hoveredWindow)) return;
var tabs = GetTabList(hoveredWindow);
var i0 = tabs.IndexOf(hoveredWindow);
var i1 = Mathf.Clamp(i0 + dir, 0, tabs.Count - 1);
var r = tabs[i0];
tabs[i0] = tabs[i1];
tabs[i1] = r;
tabs[i1].Focus();
}
void shiftscroll()
{
if (!lastEvent.shift) return;
var scrollDelta = Application.platform == RuntimePlatform.OSXEditor ? lastEvent.delta.x // osx sends delta.y as delta.x when shift is pressed
: lastEvent.delta.x - lastEvent.delta.y; // some software on windows (eg logitech options) may do that too
if (VTabsMenu.reverseScrollDirectionEnabled)
scrollDelta *= -1;
if (scrollDelta != 0)
if (lastEvent.control || lastEvent.command)
moveTab(scrollDelta > 0 ? 1 : -1);
else
switchTab(scrollDelta > 0 ? 1 : -1);
}
void sidescroll()
{
if (lastEvent.shift) return;
if (lastEvent.delta.x == 0) return;
if (lastEvent.delta.x.Abs() <= 0.06f) return;
if (lastEvent.delta.x.Abs() * 1.1f < lastEvent.delta.y.Abs()) { sidesscrollPosition = 0; return; }
if (!VTabsMenu.sidescrollEnabled) return;
var dampenK = 5; // the larger this k is - the smaller big deltas are, and the less is sidescroll's dependency on scroll speed
var a = lastEvent.delta.x.Abs() * dampenK;
var deltaDampened = (a < 1 ? a : Mathf.Log(a) + 1) / dampenK * lastEvent.delta.x.Sign();
var sensitivityK = .22f;
var sidescrollDelta = deltaDampened * VTabsMenu.sidescrollSensitivity * sensitivityK;
if (VTabsMenu.reverseScrollDirectionEnabled)
sidescrollDelta *= -1;
if (sidesscrollPosition.RoundToInt() != (sidesscrollPosition += sidescrollDelta).RoundToInt())
if (lastEvent.control || lastEvent.command)
moveTab(sidescrollDelta < 0 ? 1 : -1);
else
switchTab(sidescrollDelta < 0 ? 1 : -1);
}
shiftscroll();
sidescroll();
}
void scrollAnimation()
{
if (!EditorWindow.focusedWindow) return;
if (!EditorWindow.focusedWindow.docked) return;
if (EditorWindow.focusedWindow.maximized) return;
var dockArea = EditorWindow.focusedWindow.GetMemberValue("m_Parent");
if (dockArea.GetType() != t_DockArea) return; // happens on 2021.1.28
var curScroll = dockArea.GetFieldValue<float>("m_ScrollOffset");
if (!curScroll.Approx(0))
curScroll -= nonZeroTabScrollOffset;
if (curScroll == 0 && prevFocusedDockArea == dockArea)
curScroll = prevScroll;
var targScroll = GetOptimalTabScrollerPosition(EditorWindow.focusedWindow);
var animationSpeed = 7f;
var deltaTime = (EditorApplication.timeSinceStartup - prevTime).ToFloat().Min(.03f);
var newScroll = SmoothDamp(curScroll, targScroll, animationSpeed, ref scrollDeriv, deltaTime);
if (newScroll < .5f)
newScroll = 0;
prevScroll = newScroll;
prevFocusedDockArea = dockArea;
prevTime = EditorApplication.timeSinceStartup;
if (newScroll.Approx(curScroll)) return;
if (!newScroll.Approx(0))
newScroll += nonZeroTabScrollOffset;
dockArea.SetFieldValue("m_ScrollOffset", newScroll);
EditorWindow.focusedWindow.Repaint();
}
void createWindowDelayed()
{
if (createWindowDelayedAction == null) return;
if ((System.DateTime.UtcNow - lastDragndropTime).TotalSeconds < .05f) return;
createWindowDelayedAction.Invoke();
createWindowDelayedAction = null;
}
void dragndrop()
{
var uncaughtDragPerform = lastKnownDraggedObject != null && !DragAndDrop.objectReferences.Any() && curEvent.isMouseEnterWindow && Application.platform == RuntimePlatform.LinuxEditor;
if (!VTabsMenu.dragndropEnabled) return;
if (lastEvent.type != EventType.DragUpdated && lastEvent.type != EventType.DragPerform && !uncaughtDragPerform) return;
if (!(EditorWindow.mouseOverWindow is EditorWindow hoveredWindow)) return;
if (!hoveredWindow.position.SetPos(0, 0).SetHeight(hoveredWindow.GetType() == t_SceneHierarchyWindow ? 5 : 40).Contains(lastEvent.mousePosition)) return;
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
if (DragAndDrop.objectReferences.Any()) // workaround for null objectReferences and mouseOverWindow on linux
{
lastKnownDraggedObject = DragAndDrop.objectReferences.First();
lastKnownHoveredWindow = EditorWindow.mouseOverWindow;
}
if (lastEvent.type != EventType.DragPerform && !uncaughtDragPerform) return;
if (!lastKnownDraggedObject) return;
if (!lastKnownHoveredWindow) return;
if (lastDragndropPosition == curEvent.mousePosition) return;
DragAndDrop.AcceptDrag();
var lockToObject = lastKnownDraggedObject;
var dockArea = lastKnownHoveredWindow.GetMemberValue("m_Parent");
createWindowDelayedAction = () => new TabInfo(lockToObject).CreateWindow(dockArea, false); // not creating window right away to avoid scroll animation stutter
lastDragndropPosition = curEvent.mousePosition;
lastDragndropTime = System.DateTime.UtcNow;
lastKnownDraggedObject = null;
lastKnownHoveredWindow = null;
}
scrollInteractions();
scrollAnimation();
createWindowDelayed();
dragndrop();
CheckIfFocusedWindowChanged();
CheckIfWindowWasUnmaximized();
}
static float sidesscrollPosition;
static float scrollDeriv;
static float prevScroll;
static object prevFocusedDockArea;
static double deltaTime;
static double prevTime = 0;
static Vector2 lastDragndropPosition;
static System.DateTime lastDragndropTime;
static Action createWindowDelayedAction;
static Object lastKnownDraggedObject;
static EditorWindow lastKnownHoveredWindow;
static void CheckShortcuts() // globalEventHandler
{
void set_isKeyPressed()
{
if (curEvent.keyCode == KeyCode.LeftShift) return;
if (curEvent.keyCode == KeyCode.LeftControl) return;
if (curEvent.keyCode == KeyCode.LeftCommand) return;
if (curEvent.keyCode == KeyCode.RightShift) return;
if (curEvent.keyCode == KeyCode.RightControl) return;
if (curEvent.keyCode == KeyCode.RightCommand) return;
if (Event.current.type == EventType.KeyDown)
isKeyPressed = true;
if (Event.current.type == EventType.KeyUp)
isKeyPressed = false;
}
void addTab()
{
if (!curEvent.isKeyDown) return;
if (!curEvent.holdingCmdOnly && !curEvent.holdingCtrlOnly) return;
if (curEvent.keyCode != KeyCode.T) return;
if (!EditorWindow.mouseOverWindow) return;
if (!VTabsMenu.addTabEnabled) return;
curEvent.Use();
addTabMenu_openedOverWindow = EditorWindow.mouseOverWindow;
List<TabInfo> defaultTabList;
List<TabInfo> customTabList;
var customTabListKey = "vTabs-CustomTabList-" + GetProjectId();
void loadDefaultTabList()
{
defaultTabList = new List<TabInfo>();
defaultTabList.Add(new TabInfo("SceneView", "Scene"));
defaultTabList.Add(new TabInfo("GameView", "Game"));
defaultTabList.Add(new TabInfo("ProjectBrowser", "Project"));
defaultTabList.Add(new TabInfo("InspectorWindow", "Inspector"));
defaultTabList.Add(new TabInfo("ConsoleWindow", "Console"));
defaultTabList.Add(new TabInfo("ProfilerWindow", "Profiler"));
// defaultTabList.Add(new TabInfo("LightingWindow", "Lighting"));
// defaultTabList.Add(new TabInfo("ProjectSettingsWindow", "Project Settings"));
}
void loadSavedTabList()
{
var json = EditorPrefs.GetString(customTabListKey);
customTabList = JsonUtility.FromJson<TabInfoList>(json)?.list ?? new List<TabInfo>();
}
void saveSavedTabsList()
{
var json = JsonUtility.ToJson(new TabInfoList { list = customTabList });
EditorPrefs.SetString(customTabListKey, json);
}
loadDefaultTabList();
loadSavedTabList();
GenericMenu menu = new GenericMenu();
Vector2 menuPosition;
void rememberClickPosition()
{
addTabMenu_lastClickPosition_screenSpace = curEvent.mousePosition_screenSpace;
}
void set_menuPosition()
{
if (curEvent.mousePosition_screenSpace.DistanceTo(addTabMenu_lastClickPosition_screenSpace) < 2)
menuPosition = EditorGUIUtility.ScreenToGUIPoint(addTabMenu_lastOpenPosition_screenSpace);
else
menuPosition = curEvent.mousePosition - Vector2.up * 9;
addTabMenu_lastOpenPosition_screenSpace = EditorGUIUtility.GUIToScreenPoint(menuPosition);
#if !UNITY_2021_2_OR_NEWER
if (EditorWindow.focusedWindow)
menuPosition += EditorWindow.focusedWindow.position.position;
#endif
}
void defaultTabs()
{
menu.AddDisabledItem(new GUIContent("Default tabs"));
foreach (var tabInfo in defaultTabList)
menu.AddItem(new GUIContent(tabInfo.menuItemName), false, () =>
{
tabInfo.CreateWindow(GetDockArea(addTabMenu_openedOverWindow), false);
EditorApplication.delayCall += rememberClickPosition;
});
}
void savedTabs()
{
if (!customTabList.Any()) return;
menu.AddSeparator("");
menu.AddDisabledItem(new GUIContent("Saved tabs"));
foreach (var tabInfo in customTabList)
if (tabInfo.isPropertyEditor && !tabInfo.globalId.GetObject())
menu.AddDisabledItem(new GUIContent(tabInfo.menuItemName));
else
menu.AddItem(new GUIContent(tabInfo.menuItemName), false, () =>
{
tabInfo.CreateWindow(GetDockArea(addTabMenu_openedOverWindow), false);
EditorApplication.delayCall += rememberClickPosition;
});
}
void remove()
{
if (!customTabList.Any()) return;
foreach (var tabInfo in customTabList)
menu.AddItem(new GUIContent("Remove/" + tabInfo.menuItemName), false, () =>
{
customTabList.Remove(tabInfo);
saveSavedTabsList();
EditorApplication.delayCall += rememberClickPosition;
});
menu.AddSeparator("Remove/");
menu.AddItem(new GUIContent("Remove/Remove all"), false, () =>
{
customTabList.Clear();
saveSavedTabsList();
EditorApplication.delayCall += rememberClickPosition;
});
}
void saveCurrentTab()
{
var menuItemName = addTabMenu_openedOverWindow.titleContent.text.Replace("/", " \u2215 ").Trim(' ');
if (defaultTabList.Any(r => r.menuItemName == menuItemName)) return;
if (customTabList.Any(r => r.menuItemName == menuItemName)) return;
menu.AddSeparator("");
menu.AddItem(new GUIContent("Save current tab"), false, () =>
{
customTabList.Add(new TabInfo(addTabMenu_openedOverWindow));
saveSavedTabsList();
EditorApplication.delayCall += rememberClickPosition;
});
}
set_menuPosition();
defaultTabs();
savedTabs();
remove();
saveCurrentTab();
menu.DropDown(Rect.zero.SetPos(menuPosition));
}
void closeTab()
{
if (!curEvent.isKeyDown) return;
if (!curEvent.holdingCmdOnly && !curEvent.holdingCtrlOnly) return;
if (curEvent.keyCode != KeyCode.W) return;
if (!VTabsMenu.closeTabEnabled) return;
if (!EditorWindow.mouseOverWindow) return;
if (!EditorWindow.mouseOverWindow.docked) return;
if (GetTabList(EditorWindow.mouseOverWindow).Count <= 1) return;
Event.current.Use();
tabInfosForReopening.Push(new TabInfo(EditorWindow.mouseOverWindow));
EditorWindow.mouseOverWindow.Close();
}
void reopenTab()
{
if (!curEvent.isKeyDown) return;
if (curEvent.modifiers != (EventModifiers.Command | EventModifiers.Shift)
&& curEvent.modifiers != (EventModifiers.Control | EventModifiers.Shift)) return;
if (curEvent.keyCode != KeyCode.T) return;
if (!EditorWindow.mouseOverWindow) return;
if (!VTabsMenu.reopenTabEnabled) return;
if (!tabInfosForReopening.Any()) return;
Event.current.Use();
var window = tabInfosForReopening.Pop().CreateWindow();
UpdateTitle(window);
}
set_isKeyPressed();
addTab();
closeTab();
reopenTab();
}
static Vector2 addTabMenu_lastClickPosition_screenSpace;
static Vector2 addTabMenu_lastOpenPosition_screenSpace;
static EditorWindow addTabMenu_openedOverWindow;
static bool isKeyPressed;
static Stack<TabInfo> tabInfosForReopening = new Stack<TabInfo>();
[System.Serializable]
class TabInfo
{
public EditorWindow CreateWindow(object dockArea = null, bool atOriginalTabIndex = true)
{
if (dockArea == null) dockArea = originalDockArea;
if (dockArea == null) return null;
if (dockArea.GetType() != t_DockArea) return null; // happens in 2023.2, no idea why
var lastInteractedBrowser = t_ProjectBrowser.GetFieldValue("s_LastInteractedProjectBrowser"); // changes on new browser creation // tomove to seutup
var window = (EditorWindow)ScriptableObject.CreateInstance(typeName);
void notifyVFavorites()
{
mi_VFavorites_BeforeWindowCreated?.Invoke(null, new object[] { dockArea });
}
void addToDockArea()
{
if (atOriginalTabIndex)
dockArea.InvokeMethod("AddTab", originalTabIndex, window, true);
else
dockArea.InvokeMethod("AddTab", window, true);
}
void setupBrowser()
{
if (!isBrowser) return;
void setSavedGridSize()
{
if (!isGridSizeSaved) return;
window.GetFieldValue("m_ListArea")?.SetMemberValue("gridSize", savedGridSize);
}
void setLastUsedGridSize()
{
if (isGridSizeSaved) return;
if (lastInteractedBrowser == null) return;
var listAreaSource = lastInteractedBrowser.GetFieldValue("m_ListArea");
var listAreaDest = window.GetFieldValue("m_ListArea");
if (listAreaSource != null && listAreaDest != null)
listAreaDest.SetPropertyValue("gridSize", listAreaSource.GetPropertyValue("gridSize"));
}
void setSavedLayout()
{
if (!isLayoutSaved) return;
var layoutEnum = System.Enum.ToObject(t_ProjectBrowser.GetField("m_ViewMode", maxBindingFlags).FieldType, savedLayout);
window.InvokeMethod("SetViewMode", layoutEnum);
}
void setLastUsedLayout()
{
if (isLayoutSaved) return;
if (lastInteractedBrowser == null) return;
window.InvokeMethod("SetViewMode", lastInteractedBrowser.GetMemberValue("m_ViewMode"));
}
void setLastUsedListWidth()
{
if (lastInteractedBrowser == null) return;
window.SetFieldValue("m_DirectoriesAreaWidth", lastInteractedBrowser.GetFieldValue("m_DirectoriesAreaWidth"));
}
void lockToFolder_twoColumns()
{
if (!isLocked) return;
if (window.GetMemberValue<int>("m_ViewMode") != 1) return;
if (folderGuid.IsNullOrEmpty()) return;
var iid = AssetDatabase.LoadAssetAtPath<Object>(AssetDatabase.GUIDToAssetPath(folderGuid)).GetInstanceID();
window.GetFieldValue("m_ListAreaState").SetFieldValue("m_SelectedInstanceIDs", new List<int> { iid });
t_ProjectBrowser.InvokeMethod("OpenSelectedFolders");
window.SetPropertyValue("isLocked", true);
}
void lockToFolder_oneColumn()
{
if (!isLocked) return;
if (window.GetMemberValue<int>("m_ViewMode") != 0) return;
if (folderGuid.IsNullOrEmpty()) return;
if (!(window.GetMemberValue("m_AssetTree") is object m_AssetTree)) return;
if (!(m_AssetTree.GetMemberValue("data") is object data)) return;
var folderPath = folderGuid.ToPath();
var folderIid = AssetDatabase.LoadAssetAtPath<Object>(folderPath).GetInstanceID();
data.SetMemberValue("m_rootInstanceID", folderIid);
m_AssetTree.InvokeMethod("ReloadData");
window.GetMemberValue("m_SearchFilter")?.SetMemberValue("m_Folders", new[] { folderPath });
window.SetPropertyValue("isLocked", true);
}
window.InvokeMethod("Init");
setSavedGridSize();
setLastUsedGridSize();
setSavedLayout();
setLastUsedLayout();
setLastUsedListWidth();
lockToFolder_twoColumns();
lockToFolder_oneColumn();
UpdateBrowserTitle(window);
}
void setupPropertyEditor()
{
if (!isPropertyEditor) return;
if (globalId.isNull) return;
var lockTo = globalId.GetObject();
if (lockedPrefabAssetObject)
lockTo = lockedPrefabAssetObject; // globalId api doesn't work for prefab asset objects, so we use direct object reference in such cases
if (!lockTo) return;
window.GetMemberValue("tracker").InvokeMethod("SetObjectsLockedByThisTracker", (new List<Object> { lockTo }));
window.SetMemberValue("m_GlobalObjectId", globalId.ToString());
window.SetMemberValue("m_InspectedObject", lockTo);
UpdatePropertyEditorTitle(window);
}
void setCustomEditorWindowTitle()
{
if (window.titleContent.text != window.GetType().FullName) return;
if (originalTitle.IsNullOrEmpty()) return;
window.titleContent.text = originalTitle;
// custom EditorWindows often have their titles set in EditorWindow.GetWindow
// and when such windows are created via ScriptableObject.CreateInstance, their titles default to window type name
// so we have to set original window title in such cases
}
notifyVFavorites();
addToDockArea();
setupBrowser();
setupPropertyEditor();
setCustomEditorWindowTitle();
window.Focus();
return window;
}
public TabInfo(EditorWindow window)
{
typeName = window.GetType().Name;
originalDockArea = GetDockArea(window);
originalTabIndex = GetTabList(window).IndexOf(window);
originalTitle = window.titleContent.text;
menuItemName = window.titleContent.text.Replace("/", " \u2215 ").Trim(' ');
if (isBrowser)
{
isLocked = window.GetPropertyValue<bool>("isLocked");
savedGridSize = window.GetFieldValue<int>("m_StartGridSize");
isGridSizeSaved = true;
savedLayout = window.GetMemberValue<int>("m_ViewMode");
isLayoutSaved = true;
var folderPath = savedLayout == 0 ? window.GetMemberValue("m_SearchFilter")?.GetMemberValue<string[]>("m_Folders")?.FirstOrDefault() ?? "Assets" // one column
: window.InvokeMethod<string>("GetActiveFolderPath"); // two columns
folderGuid = folderPath.ToGuid();
}
if (isPropertyEditor)
globalId = new GlobalID(window.GetMemberValue<string>("m_GlobalObjectId"));
}
public TabInfo(Object lockTo)
{
isLocked = true;
typeName = lockTo is DefaultAsset ? t_ProjectBrowser.Name : t_PropertyEditor.Name;
if (isBrowser)
folderGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(lockTo));
if (isPropertyEditor)
globalId = lockTo.GetGlobalID();
#if UNITY_2021_2_OR_NEWER
if (isPropertyEditor)
if (StageUtility.GetCurrentStage() is PrefabStage && globalId.ToString().Contains("00000000000000000000000000000000"))
lockedPrefabAssetObject = lockTo;
#endif
}
public TabInfo(string typeName, string menuItemName) { this.typeName = typeName; this.menuItemName = menuItemName; }
public string typeName;
public string menuItemName;
public object originalDockArea;
public int originalTabIndex;
public string originalTitle;
public bool isBrowser => typeName == t_ProjectBrowser.Name;
public bool isLocked;
public string folderGuid = "";
public int savedGridSize;
public int savedLayout;
public bool isGridSizeSaved = false;
public bool isLayoutSaved = false;
public bool isPropertyEditor => typeName == t_PropertyEditor.Name;
public GlobalID globalId;
public Object lockedPrefabAssetObject;
}
[System.Serializable]
class TabInfoList { public List<TabInfo> list = new List<TabInfo>(); }
static void UpdateTitle(EditorWindow window)
{
if (window == null) return;
var isPropertyEditor = window.GetType() == t_PropertyEditor;
var isBrowser = window.GetType() == t_ProjectBrowser;
if (!isPropertyEditor && !isBrowser) return;
if (isPropertyEditor)
UpdatePropertyEditorTitle(window);
if (isBrowser)
if (window.GetPropertyValue<bool>("isLocked"))
UpdateBrowserTitle(window);
}
static void UpdateBrowserTitle(EditorWindow browser)
{
if (mi_VFavorites_CanBrowserBeWrapped != null && mi_VFavorites_CanBrowserBeWrapped.Invoke(null, new[] { browser }).Equals(false)) return;
var isLocked = browser.GetMemberValue<bool>("isLocked");
var isTitleDefault = browser.titleContent.text == "Project";
void setLockedTitle()
{
if (!isLocked) return;
var isOneColumn = browser.GetMemberValue<int>("m_ViewMode") == 0;
var path = isOneColumn ? browser.GetMemberValue("m_SearchFilter")?.GetMemberValue<string[]>("m_Folders")?.FirstOrDefault() ?? "Assets" : browser.InvokeMethod<string>("GetActiveFolderPath");
var guid = path.ToGuid();
var name = path.GetFilename();
var icon = EditorGUIUtility.FindTexture("Project");
void getIconFromVFolders()
{
if (mi_VFolders_GetIcon == null) return;
if (mi_VFolders_GetIcon.Invoke(null, new[] { guid }) is Texture2D iconFromVFolders)
icon = iconFromVFolders;
}
getIconFromVFolders();
browser.titleContent = new GUIContent(name, icon);
t_DockArea.GetFieldValue<IDictionary>("s_GUIContents").Clear();
}
void setDefaultTitle()
{
if (isLocked) return;
if (isTitleDefault) return;
var name = "Project";
var icon = EditorGUIUtility.FindTexture("Project@2x");
browser.titleContent = new GUIContent(name, icon);
t_DockArea.GetFieldValue<IDictionary>("s_GUIContents").Clear();
}
setLockedTitle();
setDefaultTitle();
}
static void UpdateBrowserTitles() => allBrowsers.ToList().ForEach(r => UpdateBrowserTitle(r));
static void UpdatePropertyEditorTitle(EditorWindow propertyEditor)
{
var obj = propertyEditor.GetMemberValue<Object>("m_InspectedObject");
if (!obj) return;
var name = obj is Component component ? GetComponentName(component) : obj.name;
var sourceIcon = AssetPreview.GetMiniThumbnail(obj);
var adjustedIcon = sourceIcon;
void getSourceIconFromVHierarchy()
{
if (mi_VHierarchy_GetIcon == null) return;
if (!(obj is GameObject gameObject)) return;
if (mi_VHierarchy_GetIcon.Invoke(null, new[] { gameObject }) is Texture2D iconFromVHierarchy)
sourceIcon = iconFromVHierarchy;
}
void getAdjustedIcon()
{
if (adjustedObjectIconsBySourceIid.TryGetValue(sourceIcon.GetInstanceID(), out adjustedIcon)) return;
adjustedIcon = new Texture2D(sourceIcon.width, sourceIcon.height, sourceIcon.format, sourceIcon.mipmapCount, false);
adjustedIcon.hideFlags = HideFlags.DontSave;
adjustedIcon.SetPropertyValue("pixelsPerPoint", (sourceIcon.width / 16f).RoundToInt());
Graphics.CopyTexture(sourceIcon, adjustedIcon);
adjustedObjectIconsBySourceIid[sourceIcon.GetInstanceID()] = adjustedIcon;
}
getSourceIconFromVHierarchy();
getAdjustedIcon();
propertyEditor.titleContent = new GUIContent(name, adjustedIcon);
propertyEditor.SetMemberValue("m_InspectedObject", null); // prevents further title updates from both internal code and vTabs
t_DockArea.GetFieldValue<IDictionary>("s_GUIContents").Clear();
}
static void UpdatePropertyEditorTitles() => allPropertyEditors.ForEach(r => UpdatePropertyEditorTitle(r));
static Dictionary<int, Texture2D> adjustedObjectIconsBySourceIid = new Dictionary<int, Texture2D>();
static void UpdateGUIWrappingForBrowser(EditorWindow browser)
{
if (!browser.hasFocus) return;
if (mi_VFavorites_CanBrowserBeWrapped != null && mi_VFavorites_CanBrowserBeWrapped.Invoke(null, new[] { browser }).Equals(false)) return;
var isLocked = browser.GetMemberValue<bool>("isLocked");
var isWrapped = browser.GetMemberValue("m_Parent").GetMemberValue<Delegate>("m_OnGUI").Method == mi_WrappedBrowserOnGUI;
void wrap()
{
if (!isLocked) return;
if (isWrapped) return;
var hostView = browser.GetMemberValue("m_Parent");
var newDelegate = typeof(VTabs).GetMethod(nameof(WrappedBrowserOnGUI), maxBindingFlags).CreateDelegate(t_EditorWindowDelegate, browser);
hostView.SetMemberValue("m_OnGUI", newDelegate);
browser.Repaint();
browser.SetMemberValue("useTreeViewSelectionInsteadOfMainSelection", false);
}
void unwrap()
{
if (isLocked) return;
if (!isWrapped) return;
var hostView = browser.GetMemberValue("m_Parent");
var originalDelegate = hostView.InvokeMethod("CreateDelegate", "OnGUI");
hostView.SetMemberValue("m_OnGUI", originalDelegate);
browser.Repaint();
}
wrap();
unwrap();
}
static void UpdateGUIWrappingForAllBrowsers() => allBrowsers.ForEach(r => UpdateGUIWrappingForBrowser(r));
static void WrappedBrowserOnGUI(EditorWindow browser)
{
var headerHeight = 26;
var footerHeight = 21;
var breadcrubsYOffset = .5f;
var headerRect = browser.position.SetPos(0, 0).SetHeight(headerHeight);
var footerRect = browser.position.SetPos(0, 0).SetHeightFromBottom(footerHeight);
var listAreaRect = browser.position.SetPos(0, 0).AddHeight(-footerHeight).AddHeightFromBottom(-headerHeight);
var breadcrumbsRect = headerRect.AddHeightFromBottom(-breadcrubsYOffset * 2);
var topGapRect = headerRect.SetHeight(breadcrubsYOffset * 2);
var breadcrumbsTint = isDarkTheme ? Greyscale(0, .05f) : Greyscale(0, .02f);
var topGapColor = isDarkTheme ? Greyscale(.24f, 1) : Greyscale(.8f, 1);
var isOneColumn = browser.GetMemberValue<int>("m_ViewMode") == 0;
void setRootForOneColumn()
{
if (!isOneColumn) return;
if (curEvent.isRepaint) return;
if (!(browser.GetMemberValue("m_AssetTree") is object m_AssetTree)) return;
if (!(m_AssetTree.GetMemberValue("data") is object data)) return;
var m_rootInstanceID = data.GetMemberValue<int>("m_rootInstanceID");
void setInitial()
{
if (m_rootInstanceID != 0) return;
var folderPath = browser.GetMemberValue("m_SearchFilter")?.GetMemberValue<string[]>("m_Folders")?.FirstOrDefault() ?? "Assets";
var folderIid = AssetDatabase.LoadAssetAtPath<Object>(folderPath).GetInstanceID();
data.SetMemberValue("m_rootInstanceID", folderIid);
m_AssetTree.InvokeMethod("ReloadData");
}
void update()
{
if (m_rootInstanceID == 0) return;
var folderIid = m_rootInstanceID;
var folderPath = EditorUtility.InstanceIDToObject(folderIid).GetPath();
browser.GetMemberValue("m_SearchFilter")?.SetMemberValue("m_Folders", new[] { folderPath });
}
void reset()
{
if (browser.GetMemberValue<bool>("isLocked")) return;
data.SetMemberValue("m_rootInstanceID", 0);
browser.GetMemberValue("m_SearchFilter")?.SetMemberValue("m_Folders", new[] { "Assets" });
m_AssetTree.InvokeMethod("ReloadData");
// returns the browser to normal state on unlock
}
setInitial();
update();
reset();
}
void handleFolderChange()
{
if (isOneColumn) return;
void onBreadcrumbsClick()
{
if (!curEvent.isMouseUp) return;
if (!breadcrumbsRect.IsHovered()) return;
browser.RecordUndo();
toCallInGUI += () => UpdateBrowserTitle(browser);
toCallInGUI += () => browser.Repaint();
}
void onDoubleclick()
{
if (!curEvent.isMouseDown) return;
if (curEvent.clickCount != 2) return;
browser.RecordUndo();
EditorApplication.delayCall += () => UpdateBrowserTitle(browser);
EditorApplication.delayCall += () => browser.Repaint();
}
void onUndoRedo()
{
if (!curEvent.isKeyDown) return;
if (!curEvent.holdingCmdOrCtrl) return;
if (curEvent.keyCode != KeyCode.Z) return;
var curFolderGuid = browser.InvokeMethod<string>("GetActiveFolderPath").ToGuid();
EditorApplication.delayCall += () =>
{
var delayedFolderGuid = browser.InvokeMethod<string>("GetActiveFolderPath").ToGuid();
if (delayedFolderGuid == curFolderGuid) return;
var folderIid = AssetDatabase.LoadAssetAtPath<Object>(AssetDatabase.GUIDToAssetPath(delayedFolderGuid)).GetInstanceID();
browser.InvokeMethod("SetFolderSelection", new[] { folderIid }, false);
UpdateBrowserTitle(browser);
};
}
onBreadcrumbsClick();
onDoubleclick();
onUndoRedo();
}
void oneColumn()
{
if (!isOneColumn) return;
if (!browser.InvokeMethod<bool>("Initialized"))
browser.InvokeMethod("Init");
var m_TreeViewKeyboardControlID = GUIUtility.GetControlID(FocusType.Keyboard);
browser.InvokeMethod("OnEvent");
if (curEvent.isMouseDown && browser.position.SetPos(0, 0).IsHovered())
t_ProjectBrowser.SetFieldValue("s_LastInteractedProjectBrowser", browser);
// header
browser.SetFieldValue("m_ListHeaderRect", breadcrumbsRect);
if (curEvent.isRepaint)
browser.InvokeMethod("BreadCrumbBar");
breadcrumbsRect.Draw(breadcrumbsTint);
topGapRect.Draw(topGapColor);
breadcrumbsRect.SetHeightFromBottom(1).Draw(Greyscale(.14f));
// footer
browser.SetFieldValue("m_BottomBarRect", footerRect);
browser.InvokeMethod("BottomBar");
// tree
browser.GetMemberValue("m_AssetTree")?.InvokeMethod("OnGUI", listAreaRect, m_TreeViewKeyboardControlID);
browser.InvokeMethod("HandleCommandEvents");
}
void twoColumns()
{
if (isOneColumn) return;
if (!browser.InvokeMethod<bool>("Initialized"))
browser.InvokeMethod("Init");
var m_ListKeyboardControlID = GUIUtility.GetControlID(FocusType.Keyboard);
var startGridSize = browser.GetFieldValue("m_ListArea")?.GetMemberValue("gridSize");
browser.InvokeMethod("OnEvent");
if (curEvent.isMouseDown && browser.position.SetPos(0, 0).IsHovered())
t_ProjectBrowser.SetFieldValue("s_LastInteractedProjectBrowser", browser);
// header
browser.SetFieldValue("m_ListHeaderRect", breadcrumbsRect);
browser.InvokeMethod("BreadCrumbBar");
breadcrumbsRect.Draw(breadcrumbsTint);
topGapRect.Draw(topGapColor);
breadcrumbsRect.SetHeightFromBottom(1).Draw(Greyscale(.14f));
// footer
browser.SetFieldValue("m_BottomBarRect", footerRect);
browser.InvokeMethod("BottomBar");
// list area
browser.GetFieldValue("m_ListArea").InvokeMethod("OnGUI", listAreaRect, m_ListKeyboardControlID);
// block grid size changes when ctrl-shift-scrolling
if (curEvent.holdingCmdOrCtrl)
browser.GetFieldValue("m_ListArea").SetMemberValue("gridSize", startGridSize);
browser.SetFieldValue("m_StartGridSize", browser.GetFieldValue("m_ListArea").GetMemberValue("gridSize"));
browser.InvokeMethod("HandleContextClickInListArea", listAreaRect);
browser.InvokeMethod("HandleCommandEvents");
}
setRootForOneColumn();
handleFolderChange();
oneColumn();
twoColumns();
}
static void ReplaceTabScrollerButtonsWithGradients()
{
void getStyles()
{
if (leftScrollerStyle != null && rightScrollerStyle != null) return;
if (!guiStylesInitialized) TryInitializeGuiStyles();
if (!guiStylesInitialized) return;
if (typeof(GUISkin).GetFieldValue("current")?.GetFieldValue<Dictionary<string, GUIStyle>>("m_Styles")?.ContainsKey("dragtab scroller prev") != true) return;
if (typeof(GUISkin).GetFieldValue("current")?.GetFieldValue<Dictionary<string, GUIStyle>>("m_Styles")?.ContainsKey("dragtab scroller next") != true) return;
var t_Styles = typeof(Editor).Assembly.GetType("UnityEditor.DockArea+Styles");
leftScrollerStyle = t_Styles.GetFieldValue<GUIStyle>("tabScrollerPrevButton");
rightScrollerStyle = t_Styles.GetFieldValue<GUIStyle>("tabScrollerNextButton");
}
void createTextures()
{
if (leftScrollerGradient != null && rightScrollerGradient != null && clearTexture != null) return;
clearTexture = new Texture2D(1, 1);
clearTexture.hideFlags = HideFlags.DontSave;
clearTexture.SetPixel(0, 0, Color.clear);
clearTexture.Apply();
var res = 16;
var greyscale = EditorGUIUtility.isProSkin ? .16f : .65f;
leftScrollerGradient = new Texture2D(res, 1);
leftScrollerGradient.hideFlags = HideFlags.DontSave;
leftScrollerGradient.SetPixels(Enumerable.Range(0, res).Select(r => Greyscale(greyscale, r / (res - 1f))).Reverse().ToArray(), 0);
leftScrollerGradient.Apply();
rightScrollerGradient = new Texture2D(res, 1);
rightScrollerGradient.hideFlags = HideFlags.DontSave;
rightScrollerGradient.SetPixels(Enumerable.Range(0, res).Select(r => Greyscale(greyscale, r / (res - 1f))).ToArray(), 0);
rightScrollerGradient.Apply();
}
void assignTextures()
{
if (leftScrollerStyle == null) return;
if (rightScrollerStyle == null) return;
// var hideLeftGradient = EditorWindow.focusedWindow && EditorWindow.focusedWindow.docked && GetTabList(EditorWindow.focusedWindow).First() == EditorWindow.focusedWindow;
// var hideRightGradient = EditorWindow.focusedWindow && EditorWindow.focusedWindow.docked && GetTabList(EditorWindow.focusedWindow).Last() == EditorWindow.focusedWindow;
var hideLeftGradient = false;
var hideRightGradient = false;
leftScrollerStyle.normal.background = hideLeftGradient ? clearTexture : leftScrollerGradient;
rightScrollerStyle.normal.background = hideRightGradient ? clearTexture : rightScrollerGradient;
}
getStyles();
createTextures();
assignTextures();
}
static GUIStyle leftScrollerStyle;
static GUIStyle rightScrollerStyle;
static Texture2D leftScrollerGradient;
static Texture2D rightScrollerGradient;
static Texture2D clearTexture;
static void ClosePropertyEditorsWithNonLoadableObjects()
{
foreach (var propertyEditor in allPropertyEditors)
if (propertyEditor.GetMemberValue<Object>("m_InspectedObject") == null)
propertyEditor.Close();
}
static void LoadPropertyEditorInspectedObjects()
{
foreach (var propertyEditor in allPropertyEditors)
propertyEditor.InvokeMethod("LoadPersistedObject");
}
static void EnsureTabVisibleOnScroller(EditorWindow window)
{
var pos = GetOptimalTabScrollerPosition(window);
if (!pos.Approx(0))
pos += nonZeroTabScrollOffset;
GetDockArea(window).SetFieldValue("m_ScrollOffset", pos);
}
static void EnsureActiveTabsVisibleOnScroller() => allEditorWindows.Where(r => r.hasFocus && !r.maximized && r.docked).ForEach(r => EnsureTabVisibleOnScroller(r));
static float GetOptimalTabScrollerPosition(EditorWindow activeTab)
{
var dockArea = activeTab.GetMemberValue("m_Parent");
var tabAreaWidth = dockArea.GetFieldValue<Rect>("m_TabAreaRect").width;
if (tabAreaWidth == 0)
tabAreaWidth = activeTab.position.width - 38;
if (tabStyle == null)
if (guiStylesInitialized)
tabStyle = new GUIStyle("dragtab");
else return 0;
var activeTabXMin = 0f;
var activeTabXMax = 0f;
var tabWidthSum = 0f;
var activeTabReached = false;
foreach (var tab in GetTabList(activeTab))
{
var tabWidth = dockArea.InvokeMethod<float>("GetTabWidth", tabStyle, tab);
tabWidthSum += tabWidth;
if (activeTabReached) continue;
activeTabXMin = activeTabXMax;
activeTabXMax += tabWidth;
if (tab == activeTab)
activeTabReached = true;
}
var optimalScrollPos = 0f;
var visibleAreaPadding = 65f;
var visibleAreaXMin = activeTabXMin - visibleAreaPadding;
var visibleAreaXMax = activeTabXMax + visibleAreaPadding;
optimalScrollPos = Mathf.Max(optimalScrollPos, visibleAreaXMax - tabAreaWidth);
optimalScrollPos = Mathf.Min(optimalScrollPos, tabWidthSum - tabAreaWidth + 4);
optimalScrollPos = Mathf.Min(optimalScrollPos, visibleAreaXMin);
optimalScrollPos = Mathf.Max(optimalScrollPos, 0);
return optimalScrollPos;
}
static GUIStyle tabStyle;
static float nonZeroTabScrollOffset = 3f;
[UnityEditor.Callbacks.PostProcessBuild]
static void OnBuild(BuildTarget _, string __)
{
EditorApplication.delayCall += LoadPropertyEditorInspectedObjects;
EditorApplication.delayCall += UpdatePropertyEditorTitles;
}
static void OnDomainReloaded()
{
toCallInGUI += UpdateGUIWrappingForAllBrowsers;
toCallInGUI += UpdateBrowserTitles;
}
static void OnSceneOpened(Scene _, OpenSceneMode __)
{
LoadPropertyEditorInspectedObjects();
ClosePropertyEditorsWithNonLoadableObjects();
UpdatePropertyEditorTitles();
}
static void OnProjectLoaded()
{
toCallInGUI += EnsureActiveTabsVisibleOnScroller;
UpdatePropertyEditorTitles();
}
static void OnFocusedWindowChanged()
{
if (EditorWindow.focusedWindow?.GetType() == t_ProjectBrowser)
UpdateGUIWrappingForBrowser(EditorWindow.focusedWindow);
}
static void OnWindowUnmaximized()
{
UpdatePropertyEditorTitles();
UpdateBrowserTitles();
UpdateGUIWrappingForAllBrowsers();
EnsureActiveTabsVisibleOnScroller();
}
static void CheckIfFocusedWindowChanged()
{
if (prevFocusedWindow != EditorWindow.focusedWindow)
OnFocusedWindowChanged();
prevFocusedWindow = EditorWindow.focusedWindow;
}
static EditorWindow prevFocusedWindow;
static void CheckIfWindowWasUnmaximized()
{
var isMaximized = EditorWindow.focusedWindow?.maximized == true;
if (!isMaximized && wasMaximized)
OnWindowUnmaximized();
wasMaximized = isMaximized;
}
static bool wasMaximized;
static void OnSomeGUI()
{
toCallInGUI?.Invoke();
toCallInGUI = null;
CheckIfFocusedWindowChanged();
}
static void ProjectWindowItemOnGUI(string _, Rect __) => OnSomeGUI();
static void HierarchyWindowItemOnGUI(int _, Rect __) => OnSomeGUI();
static Action toCallInGUI;
static void DelayCallLoop()
{
UpdateBrowserTitles();
UpdateGUIWrappingForAllBrowsers();
UpdateDelayedMousePosition();
ReplaceTabScrollerButtonsWithGradients();
EditorApplication.delayCall -= DelayCallLoop;
EditorApplication.delayCall += DelayCallLoop;
}
static void UpdateDelayedMousePosition()
{
var lastEvent = typeof(Event).GetFieldValue<Event>("s_Current");
delayedMousePosition_screenSpace = EditorGUIUtility.GUIToScreenPoint(lastEvent.mousePosition);
}
static Vector2 delayedMousePosition_screenSpace;
static void ComponentTabHeaderGUI(Editor editor)
{
if (!(editor.target is Component component)) return;
var headerRect = ExpandWidthLabelRect(height: 0).MoveY(-48).SetHeight(50).AddWidthFromMid(8);
var nameRect = headerRect.MoveX(43).MoveY(5).SetHeight(20).SetXMax(headerRect.xMax - 50);
var subtextRect = headerRect.MoveX(43).MoveY(22).SetHeight(20);
void hideName()
{
var maskRect = headerRect.AddWidthFromRight(-45).AddWidth(-50);
var maskColor = Greyscale(isDarkTheme ? .24f : .8f);
maskRect.Draw(maskColor);
}
void name()
{
SetLabelFontSize(13);
GUI.Label(nameRect, GetComponentName(component));
ResetLabelStyle();
}
void componentOf()
{
SetGUIEnabled(false);
GUI.Label(subtextRect, "Component of");
ResetGUIEnabled();
}
void goName()
{
var goNameRect = subtextRect.MoveX("Component of ".GetLabelWidth() - 3).SetWidth(component.gameObject.name.GetLabelWidth(isBold: true));
goNameRect.MarkInteractive();
SetGUIEnabled(goNameRect.IsHovered() && !mousePressedOnGoName);
SetLabelBold();
GUI.Label(goNameRect, component.gameObject.name);
ResetGUIEnabled();
ResetLabelStyle();
if (curEvent.isMouseDown && goNameRect.IsHovered())
{
mousePressedOnGoName = true;
curEvent.Use();
}
if (curEvent.isMouseUp)
{
if (mousePressedOnGoName)
EditorGUIUtility.PingObject(component.gameObject);
mousePressedOnGoName = false;
curEvent.Use();
}
if (curEvent.isMouseLeaveWindow || (!curEvent.isLayout && !goNameRect.Resize(1).IsHovered()))
mousePressedOnGoName = false;
}
hideName();
name();
componentOf();
goName();
Space(-4);
}
static bool mousePressedOnGoName;
static string GetComponentName(Component component)
{
if (!component) return "";
var name = new GUIContent(EditorGUIUtility.ObjectContent(component, component.GetType())).text;
name = name.Substring(name.LastIndexOf('(') + 1);
name = name.Substring(0, name.Length - 1);
return name;
}
static void PreventGameViewZoomOnShiftScroll() // called from Update
{
if (!curEvent.holdingShift) return;
if (Application.isPlaying) return; // zoom by scrolling is disabled in playmode anyway
if (!(EditorWindow.mouseOverWindow is EditorWindow hoveredWindow)) return;
if (hoveredWindow.GetType() != t_GameView) return;
if (!(hoveredWindow.GetMemberValue("m_ZoomArea", false) is object zoomArea)) return;
var isScroll = !curEvent.isMouseMove && curEvent.mouseDelta != Vector2.zero;
if (isScroll)
{
zoomArea.SetMemberValue("m_Scale", lastGameViewScale);
zoomArea.SetMemberValue("m_Translation", lastGameViewTranslation);
}
else
{
lastGameViewScale = zoomArea.GetMemberValue<Vector2>("m_Scale");
lastGameViewTranslation = zoomArea.GetMemberValue<Vector2>("m_Translation");
}
}
static Vector2 lastGameViewScale = Vector2.one;
static Vector2 lastGameViewTranslation = Vector2.zero;
static void TryInitializeGuiStyles() => EditorWindow.focusedWindow?.SendEvent(EditorGUIUtility.CommandEvent(""));
static bool guiStylesInitialized => typeof(GUI).GetFieldValue("s_Skin") != null;
static object GetDockArea(EditorWindow window) => window.GetFieldValue("m_Parent");
static List<EditorWindow> GetTabList(EditorWindow window) => GetDockArea(window).GetFieldValue<List<EditorWindow>>("m_Panes");
[InitializeOnLoadMethod]
static void Init()
{
if (VTabsMenu.pluginDisabled) return;
// dragndrop and scrolling
EditorApplication.delayCall += () => EditorApplication.update -= Update;
EditorApplication.delayCall += () => EditorApplication.update += Update;
EditorApplication.delayCall -= UpdateDelayedMousePosition;
EditorApplication.delayCall += UpdateDelayedMousePosition;
EditorApplication.update -= PreventGameViewZoomOnShiftScroll;
EditorApplication.update += PreventGameViewZoomOnShiftScroll;
// shortcuts
var globalEventHandler = typeof(EditorApplication).GetFieldValue<EditorApplication.CallbackFunction>("globalEventHandler");
typeof(EditorApplication).SetFieldValue("globalEventHandler", (globalEventHandler - CheckShortcuts) + CheckShortcuts);
// component tabs
Editor.finishedDefaultHeaderGUI -= ComponentTabHeaderGUI;
Editor.finishedDefaultHeaderGUI += ComponentTabHeaderGUI;
// state change detectors
var projectWasLoaded = typeof(EditorApplication).GetFieldValue<UnityEngine.Events.UnityAction>("projectWasLoaded");
typeof(EditorApplication).SetFieldValue("projectWasLoaded", (projectWasLoaded - OnProjectLoaded) + OnProjectLoaded);
UnityEditor.SceneManagement.EditorSceneManager.sceneOpened -= OnSceneOpened;
UnityEditor.SceneManagement.EditorSceneManager.sceneOpened += OnSceneOpened;
EditorApplication.projectWindowItemOnGUI -= ProjectWindowItemOnGUI;
EditorApplication.projectWindowItemOnGUI += ProjectWindowItemOnGUI;
EditorApplication.hierarchyWindowItemOnGUI -= HierarchyWindowItemOnGUI;
EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowItemOnGUI;
EditorApplication.delayCall -= DelayCallLoop;
EditorApplication.delayCall += DelayCallLoop;
OnDomainReloaded();
}
static IEnumerable<EditorWindow> allBrowsers => _allBrowsers ??= t_ProjectBrowser.GetFieldValue<IList>("s_ProjectBrowsers").Cast<EditorWindow>();
static IEnumerable<EditorWindow> _allBrowsers;
static IEnumerable<EditorWindow> allPropertyEditors => Resources.FindObjectsOfTypeAll(t_PropertyEditor).Where(r => r.GetType().BaseType == typeof(EditorWindow)).Cast<EditorWindow>();
static List<EditorWindow> allEditorWindows => Resources.FindObjectsOfTypeAll<EditorWindow>().ToList();
static Type t_EditorWindow = typeof(EditorWindow);
static Type t_DockArea = typeof(Editor).Assembly.GetType("UnityEditor.DockArea");
static Type t_PropertyEditor = typeof(Editor).Assembly.GetType("UnityEditor.PropertyEditor");
static Type t_ProjectBrowser = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser");
static Type t_GameView = typeof(Editor).Assembly.GetType("UnityEditor.GameView");
static Type t_SceneHierarchyWindow = typeof(Editor).Assembly.GetType("UnityEditor.SceneHierarchyWindow");
static Type t_HostView = typeof(Editor).Assembly.GetType("UnityEditor.HostView");
static Type t_EditorWindowDelegate = t_HostView.GetNestedType("EditorWindowDelegate", maxBindingFlags);
static MethodInfo mi_WrappedBrowserOnGUI = typeof(VTabs).GetMethod(nameof(WrappedBrowserOnGUI), maxBindingFlags);
static Type t_VHierarchy = Type.GetType("VHierarchy.VHierarchy") ?? Type.GetType("VHierarchy.VHierarchy, VHierarchy, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
static Type t_VFolders = Type.GetType("VFolders.VFolders") ?? Type.GetType("VFolders.VFolders, VFolders, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
static Type t_VFavorites = Type.GetType("VFavorites.VFavorites") ?? Type.GetType("VFavorites.VFavorites, VFavorites, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
static MethodInfo mi_VFolders_GetIcon = t_VFolders?.GetMethod("GetSmallFolderIcon_forVTabs", maxBindingFlags);
static MethodInfo mi_VHierarchy_GetIcon = t_VHierarchy?.GetMethod("GetIcon_forVTabs", maxBindingFlags);
static MethodInfo mi_VFavorites_BeforeWindowCreated = t_VFavorites?.GetMethod("BeforeWindowCreated_byVTabs", maxBindingFlags);
static MethodInfo mi_VFavorites_CanBrowserBeWrapped = t_VFavorites?.GetMethod("CanBrowserBeWrapped_byVTabs", maxBindingFlags);
const string version = "2.0.14";
}
}
#endif