Files
BABA_YAGA/Assets/Third Parties/vFavorites/VFavorites.cs
2026-06-09 02:05:00 +07:00

2072 lines
69 KiB
C#

#if UNITY_EDITOR
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.SceneManagement;
using UnityEditor.SceneManagement;
using UnityEditor.ShortcutManagement;
using System.Reflection;
using System.Linq;
using UnityEngine.UIElements;
using Type = System.Type;
using static VFavorites.Libs.VUtils;
using static VFavorites.Libs.VGUI;
using static VFavorites.VFavoritesData;
namespace VFavorites
{
public static class VFavorites
{
static void WrappedOnGUI(object _)
{
if (wrappedBrowser?.GetType() != t_BrowserWindow) { originalBrowserGUI(); return; }
void createData()
{
if (data) return;
data = ScriptableObject.CreateInstance<VFavoritesData>();
AssetDatabase.CreateAsset(data, GetScriptPath("VFavorites").GetParentPath().CombinePath("vFavorites Data.asset"));
// migrateDataFromV1
if (!EditorPrefs.HasKey("vFavorites-guids-" + GetProjectId())) return;
if (EditorPrefs.HasKey("vHierarchy-dataMigrationFromV1Attempted-" + GetProjectId())) return;
EditorPrefs.SetBool("vHierarchy-dataMigrationFromV1Attempted-" + GetProjectId(), true);
if (!data.pages.Any())
data.pages.Add(new Page("Page 1"));
var guidsFromV1 = EditorPrefs.GetString("vFavorites-guids-" + GetProjectId()).Split('-').Where(r => r != "").ToList();
foreach (var guid in guidsFromV1)
if (AssetDatabase.LoadAssetAtPath<Object>(guid.ToPath()) is Object obj)
data.pages.First().items.Add(new Item(obj));
data.Dirty();
data.Save();
}
void background()
{
var color = isDarkTheme ? Greyscale(.2f) : Greyscale(.78f);
totalRect_groupSpace.Draw(color);
}
void pages()
{
void page(Rect pageRect, Page page)
{
void findSelectedItem()
{
if (!curEvent.isLayout) return;
foreach (var item in page.items)
item.isSelected = false;
if (draggingItem) return;
if (mousePresesdOnItem) return;
if (page.lastItemDragTime_ticks > page.lastItemSelectTime_ticks) return;
Item lastSelectedItem = null;
foreach (var item in page.items)
{
if (!item.isLoadable) continue;
if (lastSelectedItem?.lastSelectTime_ticks > item.lastSelectTime_ticks) continue;
var isSelected = false;
if (item.isFolder)
{
var targetBrowser = isWrappedBrowserLocked && isOneColumn ? allBrowsers.FirstOrDefault(r => !r.GetMemberValue<bool>("isLocked")) ?? wrappedBrowser
: wrappedBrowser;
if (targetBrowser.GetFieldValue<int>("m_ViewMode") == 1)
isSelected = targetBrowser.InvokeMethod<string>("GetActiveFolderPath") == item.assetPath;
else
isSelected = Selection.activeObject == item.obj;
}
if (!item.isFolder)
isSelected = Selection.activeObject == item.obj;
if (isSelected)
lastSelectedItem = item;
}
if (lastSelectedItem != null)
lastSelectedItem.isSelected = true;
}
void rows()
{
void row(float y, Item item)
{
var rowRect = pageRect.SetHeight(rowHeight).SetY(y).SetX(0);
var iconOffset = 6;
var iconSize = 25 * Mathf.Min(1, data.rowScale);
var nameOffset = 3;
var deletedOrNotLoadedLabelOffset = 1;
float highlightAmount = 0f;
void set_highlightAmount()
{
if (animatingDroppedItem && item == droppedItem)
highlightAmount = droppedItemHighlightAmount;
if (item.isSelected)
highlightAmount = 1;
if (draggingItem && item == draggedItem)
highlightAmount = 1;
if (mousePresesdOnItem && item == pressedItem)
highlightAmount = 1;
}
void shadow()
{
if (item != draggedItem && item != droppedItem) return;
var amount = item == droppedItem ? droppedItemShadowAmount : 1;
if (amount.Approx(0)) return;
rowRect.AddWidthFromMid(30).DrawBlurred(Greyscale(0, .55f * amount), 22);
}
void background()
{
var evenColor = isDarkTheme ? Greyscale(.249f) : Greyscale(.82f);
var oddColor = isDarkTheme ? Greyscale(.228f) : Greyscale(.85f);
var highlightedColor = isDarkTheme ? Greyscale(.335f) : Greyscale(.9f);
var rowColor = Lerp(evenColor, oddColor, rowRect.y.PingPong(rowHeight) / rowHeight);
Lerp(ref rowColor, highlightedColor, highlightAmount);
rowRect.Draw(rowColor);
}
void icon()
{
var iconRect = rowRect.MoveX(iconOffset).SetWidth(iconSize).SetHeightFromMid(iconSize);
if (item.isSceneGameObject)
iconRect = iconRect.MoveX(1).Resize(.5f);
void asset()
{
if (!item.isAsset) return;
var iconTexture = item.isLoadable ? AssetPreview.GetAssetPreview(item.obj) ?? AssetPreview.GetMiniThumbnail(item.obj) : AssetPreview.GetMiniTypeThumbnail(item.type);
GUI.DrawTexture(iconRect, iconTexture);
}
void sceneGameObject()
{
if (!item.isSceneGameObject) return;
void getIconNameFromAssetPreview()
{
if (!item.isLoadable) return;
item.sceneGameObjectIconName = AssetPreview.GetMiniThumbnail(item.obj).name;
}
void getIconNameFromVHierarchy()
{
if (!item.isLoadable) return;
if (!(item.obj is GameObject gameObject)) return;
if (mi_VHierarchy_GetIconName == null) return;
var iconNameFromVHierarchy = (string)mi_VHierarchy_GetIconName.Invoke(null, new object[] { gameObject });
if (!iconNameFromVHierarchy.IsNullOrEmpty())
item.sceneGameObjectIconName = iconNameFromVHierarchy;
}
getIconNameFromAssetPreview();
getIconNameFromVHierarchy();
var iconTexture = EditorGUIUtility.IconContent(item.sceneGameObjectIconName.IsNullOrEmpty() ? "GameObject icon" : item.sceneGameObjectIconName).image;
GUI.DrawTexture(iconRect, iconTexture);
}
void folder()
{
if (!item.isFolder) return;
iconRect = iconRect.Resize(-1.5f);
void drawNormal()
{
if (isDarkTheme)
if (highlightAmount == 1) return;
GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("Folder icon").image);
}
void drawHighlighted()
{
if (!isDarkTheme) return;
if (highlightAmount != 1) return;
SetGUIColor(Greyscale(.84f));
GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("Folder On icon").image);
ResetGUIColor();
}
void drawViaVFolders()
{
mi_VFolders_DrawBigFolderIcon?.Invoke(null, new object[] { iconRect, item.globalId.guid });
}
drawNormal();
drawHighlighted();
drawViaVFolders();
}
asset();
sceneGameObject();
folder();
}
void name()
{
var nameRect = rowRect.MoveX(iconOffset + iconSize + nameOffset).MoveY(-.5f).SetHeightFromMid(16);
void normal()
{
if (isDarkTheme)
if (highlightAmount == 1) return;
GUI.Label(nameRect, item.name);
}
void highlighted()
{
if (!isDarkTheme) return;
if (highlightAmount != 1) return;
if (!curEvent.isRepaint) return;
SetGUIColor(Greyscale(.91f));
GUI.skin.GetStyle("WhiteLabel").Draw(nameRect, item.name, false, false, false, false);
ResetGUIColor();
}
normal();
highlighted();
}
void deletedOrNotLoaded()
{
var labelRect = rowRect.MoveX(iconOffset + iconSize + nameOffset + item.name.GetLabelWidth() + deletedOrNotLoadedLabelOffset).MoveY(.5f);
SetGUIEnabled(false);
SetLabelFontSize(10);
if (item.isDeleted)
GUI.Label(labelRect, "Deleted");
else if (!item.isLoadable)
GUI.Label(labelRect, "Not loaded");
ResetLabelStyle();
ResetGUIEnabled();
}
void crossButton()
{
if (!rowRect.IsHovered()) return;
if (draggingItem) return;
// if (mousePresesdOnItem) return; // idk
var buttonRect = rowRect.SetWidthFromRight(0).MoveX(-crossButtonOffsetFromRight).SetWidthFromMid(crossButtonSize);
var iconRect = buttonRect.SetSizeFromMid(16);
var normalColor = Greyscale(item.isSelected ? .48f : .4f);
var hoveredColor = isDarkTheme ? Greyscale(.8f) : normalColor;
var pressedColor = Greyscale(.6f);
SetGUIColor(buttonRect.IsHovered() ? (mousePressed ? pressedColor : hoveredColor) : normalColor);
GUI.Label(iconRect, EditorGUIUtility.IconContent("CrossIcon"));
ResetGUIColor();
if (!mousePressedOnCrossButtonArea) return;
if (!curEvent.isMouseUp) return;
if (!buttonRect.IsHovered()) return;
CancelRowAnimations();
data.curPage.rowGaps[data.curPage.items.IndexOf(item)] = rowHeight;
data.curPage.items.Remove(item);
data.Dirty();
data.Save();
curEvent.Use();
}
void click()
{
if (!rowRect.IsHovered()) return;
if (!curEvent.isMouseUp) return;
curEvent.Use();
if (draggingItem) return;
if (mouseDragDistance > 2) return;
if (!item.isLoadable) return;
SelectItem(item);
}
void doubleclick()
{
if (!rowRect.IsHovered()) return;
if (!doubleclickUnhandled) return;
OpenItem(item);
doubleclickUnhandled = false;
}
set_highlightAmount();
shadow();
background();
icon();
name();
deletedOrNotLoaded();
crossButton();
click();
doubleclick();
}
void normalRow(int i)
{
Space(page.rowGaps[i]);
Space(rowHeight);
if (page.items[i] == droppedItem && animatingDroppedItem && page == data.curPage) return;
row(lastRect.y, page.items[i]);
}
void draggedRow()
{
if (!draggingItem) return;
if (page != data.curPage) return;
row(draggedItemY_rowsSpace, draggedItem);
}
void droppedRow()
{
if (!animatingDroppedItem) return;
if (page != data.curPage) return;
row(droppedItemY_rowsSpace, droppedItem);
}
if (curEvent.isRepaint && skipNextRepaint) { skipNextRepaint = false; return; }
GUILayout.BeginArea(pageRect);
page.scrollPos = EditorGUILayout.BeginScrollView(new Vector2(0, page.scrollPos), GUIStyle.none, GUIStyle.none).y;
for (int i = 0; i < page.items.Count; i++)
normalRow(i);
Space(page.rowGaps.Last());
Space(60);
draggedRow();
droppedRow();
EditorGUILayout.EndScrollView();
GUILayout.EndArea();
}
void curtains()
{
var height = 25;
var color = isDarkTheme ? Greyscale(.2f) : Greyscale(.78f);
pageRect.SetHeight(height).DrawCurtainDown(color.SetAlpha((page.scrollPos / 20).Smoothstep()));
pageRect.SetHeightFromBottom(height).DrawCurtainUp(color);
}
void tutor()
{
if (page.items.Any() || draggingItem) return;
SetGUIEnabled(false);
SetLabelFontSize(11);
SetLabelAlignmentCenter();
GUI.Label(pageRect.MoveY(-13), "Drop folders, assets");
GUI.Label(pageRect.MoveY(5), "or GameObjects");
ResetGUIEnabled();
ResetLabelStyle();
}
findSelectedItem();
rows();
curtains();
tutor();
}
var spaceBetweenPages = 10;
if (pagesScrollPos == -1)
pagesScrollPos = data.curPageIndex;
for (int i = pagesScrollPos.FloorToInt(); i <= pagesScrollPos.CeilToInt(); i++)
page(totalRect_groupSpace.SetX((totalRect_groupSpace.width + spaceBetweenPages) * (i - pagesScrollPos)), data.pages[i]);
}
void widget()
{
var widthToAdd = 48;
var height = 24;
var distToBottom = 13;
var color = isDarkTheme ? Greyscale(.1f) : Greyscale(.85f);
var shadowSize = 10;
var shadowAlpha = .23f;
var chevronSize = 15;
var chevronOffset = 12;
var chevronBrightness = .5f;
var textSize = 11;
var textBrightness = .65f;
widgetRect_groupSpace = totalRect_groupSpace.SetWidthFromMid(data.curPage.name.GetLabelWidth(textSize) + widthToAdd).SetHeightFromBottom(height).MoveY(-distToBottom);
void shadow()
{
widgetRect_groupSpace.Resize(1).DrawBlurred(Greyscale(0f, shadowAlpha), shadowSize);
}
void background()
{
widgetRect_groupSpace.DrawWithRoundedCorners(color, 1223);
}
void nameLabel()
{
if (renamingPage) return;
var buttonRect = widgetRect_groupSpace.SetWidthFromMid(data.curPage.name.GetLabelWidth(textSize) - 2);
var activated = curEvent.isMouseUp && buttonRect.IsHovered();
var brightness = !buttonRect.IsHovered() ? textBrightness : (mousePressed ? .75f : 1);
SetGUIColor(Greyscale(brightness));
SetLabelAlignmentCenter();
SetLabelFontSize(textSize);
SetLabelBold();
GUI.Label(widgetRect_groupSpace, data.curPage.name);
ResetGUIColor();
ResetLabelStyle();
if (!activated) return;
renamingPage = true;
prevPageName = data.curPage.name;
curEvent.Use();
}
void leftButton()
{
if (renamingPage) return;
var iconRect = widgetRect_groupSpace.SetWidth(chevronOffset * 2).SetSizeFromMid(chevronSize, chevronSize);
var buttonRect = Rect.zero.SetX(0).SetY(widgetRect_groupSpace.y).SetXMax(iconRect.xMax + 4).SetYMax(totalRect_groupSpace.yMax);
var active = data.curPageIndex > 0;
var activated = curEvent.isMouseUp && buttonRect.IsHovered();
var brightness = !buttonRect.IsHovered() || !active ? chevronBrightness : (mousePressed ? .75f : 1);
SetGUIColor(Greyscale(brightness));
GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("NodeChevronLeft@2x").image);
ResetGUIColor();
if (!activated) return;
curEvent.Use();
if (!active) return;
CancelDragging();
CancelRowAnimations();
data.curPageIndex--;
}
void rightButton()
{
if (renamingPage) return;
var iconRect = widgetRect_groupSpace.SetWidthFromRight(chevronOffset * 2).SetSizeFromMid(chevronSize, chevronSize);
var buttonRect = Rect.zero.SetX(iconRect.x - 6).SetY(widgetRect_groupSpace.y).SetXMax(totalRect_groupSpace.xMax).SetYMax(totalRect_groupSpace.yMax);
var active = true;// data.curPageIndex < data.pages.Count - 1 && data.curPage.items.Any();
var activated = curEvent.isMouseUp && buttonRect.IsHovered();
var brightness = !buttonRect.IsHovered() || !active ? chevronBrightness : (mousePressed ? .75f : 1);
SetGUIColor(Greyscale(brightness));
GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("NodeChevronRight@2x").image);
ResetGUIColor();
if (!activated) return;
curEvent.Use();
if (!active) return;
CancelDragging();
CancelRowAnimations();
data.curPageIndex++;
}
void nameTextField()
{
if (!renamingPage) return;
var textFieldRect = widgetRect_groupSpace.AddHeightFromMid(-2).SetWidthFromMid(data.curPage.name.GetLabelWidth(textSize) + 4);
var s = new GUIStyle(GUI.skin.textField);
s.alignment = TextAnchor.MiddleCenter;
s.fontSize = 11;
EditorGUIUtility.AddCursorRect(textFieldRect, MouseCursor.CustomCursor);
GUI.SetNextControlName("asdasdasd");
EditorGUI.FocusTextInControl("asdasdasd");
data.curPage.name = EditorGUI.TextField(textFieldRect, data.curPage.name, s);
if (data.curPage.name.IsNullOrEmpty())
data.curPage.name = "Page " + (data.curPageIndex + 1);
EditorGUIUtility.AddCursorRect(textFieldRect, MouseCursor.CustomCursor);
}
void cancelRename_button()
{
if (!renamingPage) return;
var iconRect = widgetRect_groupSpace.SetWidth(chevronOffset * 2).SetSizeFromMid(chevronSize - 2);
var buttonRect = iconRect.SetSizeFromMid(25, 25);
var brightness = !buttonRect.IsHovered() ? chevronBrightness : (mousePressed ? .75f : 1);
SetGUIColor(Greyscale(brightness));
GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("CrossIcon").image);
ResetGUIColor();
if (!curEvent.isMouseUp) return;
if (!buttonRect.IsHovered()) return;
curEvent.Use();
data.curPage.name = prevPageName;
renamingPage = false;
}
void acceptRename_button()
{
if (!renamingPage) return;
var iconRect = widgetRect_groupSpace.SetWidthFromRight(chevronOffset * 2).SetSizeFromMid(chevronSize - 0);
var buttonRect = iconRect.SetSizeFromMid(25, 25);
var brightness = !buttonRect.IsHovered() ? chevronBrightness : (mousePressed ? .75f : 1);
SetGUIColor(Greyscale(brightness));
GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("check").image);
ResetGUIColor();
if (!curEvent.isMouseUp) return;
if (!buttonRect.IsHovered()) return;
curEvent.Use();
renamingPage = false;
}
void acceptRename_enterKey()
{
if (!renamingPage) return;
if (curEvent.keyCode != KeyCode.Return) return;
renamingPage = false;
}
void cancelRename_escapeKey()
{
if (!renamingPage) return;
if (curEvent.keyCode != KeyCode.Escape) return;
data.curPage.name = prevPageName;
renamingPage = false;
}
shadow();
background();
leftButton();
rightButton();
nameLabel();
nameTextField();
cancelRename_button();
acceptRename_button();
acceptRename_enterKey();
cancelRename_escapeKey();
}
void keys()
{
if (isWrappedBrowserLocked && !totalRect_browserSpace.IsHovered()) return;
void prevPage()
{
if (!curEvent.isKeyDown) return;
if (curEvent.keyCode != KeyCode.LeftArrow) return;
if (!VFavoritesMenu.changePagesWithArrowsEnabled) return;
if (data.curPageIndex == 0) return;
CancelDragging();
CancelRowAnimations();
data.curPageIndex--;
curEvent.Use();
}
void nextPage()
{
if (!curEvent.isKeyDown) return;
if (curEvent.keyCode != KeyCode.RightArrow) return;
if (!VFavoritesMenu.changePagesWithArrowsEnabled) return;
CancelDragging();
CancelRowAnimations();
data.curPageIndex++;
curEvent.Use();
}
void selectPrev()
{
if (!curEvent.isKeyDown) return;
if (curEvent.keyCode != KeyCode.UpArrow) return;
if (!VFavoritesMenu.changeSelectionWithArrowsEnabled) return;
var iToSelect = data.curPage.items.IndexOfFirst(r => r.isSelected) - 1;
if (iToSelect.IsInRangeOf(data.curPage.items))
SelectItem(data.curPage.items[iToSelect]);
curEvent.Use();
}
void selectNext()
{
if (!curEvent.isKeyDown) return;
if (curEvent.keyCode != KeyCode.DownArrow) return;
if (!VFavoritesMenu.changeSelectionWithArrowsEnabled) return;
var iToSelect = data.curPage.items.IndexOfFirst(r => r.isSelected) + 1;
if (iToSelect.IsInRangeOf(data.curPage.items))
SelectItem(data.curPage.items[iToSelect]);
curEvent.Use();
}
void selectByNumberKey()
{
if (!curEvent.isKeyDown) return;
if (!VFavoritesMenu.setSelectionWithNumberKeysEnabled) return;
if (EditorGUIUtility.editingTextField) return;
var i = ((int)curEvent.keyCode - 49);
if (!i.IsInRange(0, 9)) return;
if (!i.IsInRangeOf(data.curPage.items)) return;
if (i == lastNumberKeyPressedIndex && EditorApplication.timeSinceStartup - lastNumberKeyPressedTime_ticks < .3f)
OpenItem(data.curPage.items[i]);
else
SelectItem(data.curPage.items[i]);
lastNumberKeyPressedIndex = i;
lastNumberKeyPressedTime_ticks = System.DateTime.UtcNow.Ticks;
curEvent.Use();
}
prevPage();
nextPage();
selectPrev();
selectNext();
selectByNumberKey();
}
void onRepaint()
{
if (!curEvent.isRepaint) return;
originalBrowserGUI();
GUI.BeginGroup(totalRect_browserSpace);
GUI.color = GUI.color.SetAlpha(currentOpacity);
background();
pages();
widget();
GUI.color = GUI.color.SetAlpha(1);
GUI.EndGroup();
}
void onOtherEvents()
{
if (curEvent.isRepaint) return;
keys();
GUI.BeginGroup(totalRect_browserSpace);
widget();
pages();
GUI.EndGroup();
if (totalRect_browserSpace.IsHovered())
if (curEvent.isMouseUp || curEvent.isMouseDrag || curEvent.isScroll) // prevents these events from reaching original gui
curEvent.Use();
originalBrowserGUI();
}
void originalBrowserGUI()
{
if (origBrowserOnGUIDelegate.GetMethodInfo().DeclaringType.Name.Contains("VTabs")) return;
if (isOneColumn && currentOpacity.Approx(1)) // to optimize locked one-column browser functioning as a dedicated favorites window
if (originalGUICalledOnce) // needs to be called once to init stuff so pinging object won't throw exceptions
return;
origBrowserOnGUIDelegate.GetMethodInfo().Invoke(wrappedBrowser, null);
originalGUICalledOnce = true;
}
createData();
UpdateMouseState();
UpdateDragging();
UpdateAnimations();
onRepaint();
onOtherEvents();
if (isWrappedBrowserLocked)
if (!totalRect_browserSpace.IsHovered() && !animatingDroppedItem && !animatingPageScroll) return;
wrappedBrowser.Repaint();
}
static void SelectItem(Item item)
{
void openFolder(EditorWindow browser, string path)
{
if (!browser) return;
var folderAsset = AssetDatabase.LoadAssetAtPath<Object>(path);
if (!folderAsset) return;
var folderInstanceId = folderAsset.GetInstanceID();
if (browser.GetFieldValue<int>("m_ViewMode") == 1)
{
TryOpenFolderInProjectBrowser(browser, folderInstanceId);
Selection.activeObject = folderAsset;
EditorGUIUtility.PingObject(folderAsset);
}
else
{
Selection.activeObject = folderAsset;
TryInvoke(browser.GetMemberValue("m_AssetTree")?.GetPropertyValue("data", exceptionIfNotFound: false), "SetExpanded", folderInstanceId, true);
}
browser.Repaint();
}
void selectSceneObject()
{
if (!item.isSceneGameObject) return;
Selection.activeObject = item.obj;
}
void selectAsset_twoColumns()
{
if (!item.isAsset) return;
if (wrappedBrowser.GetFieldValue<int>("m_ViewMode") != 1) return;
openFolder(wrappedBrowser, item.assetPath.GetParentPath());
Selection.activeObject = item.obj;
}
void selectAsset_oneColumn()
{
if (!item.isAsset) return;
if (wrappedBrowser.GetFieldValue<int>("m_ViewMode") != 0) return;
Selection.activeObject = item.obj;
}
void openFolder_unlocked()
{
if (!item.isFolder) return;
if (isWrappedBrowserLocked) return;
openFolder(wrappedBrowser, item.assetPath);
}
void openFolder_locked()
{
if (!item.isFolder) return;
if (!isWrappedBrowserLocked) return;
var unlockedBrowser = allBrowsers.FirstOrDefault(r => !r.GetMemberValue<bool>("isLocked"));
var browserToUse = isOneColumn ? unlockedBrowser : lockedBrowser;
if (!browserToUse) return;
openFolder(browserToUse, item.assetPath);
}
selectSceneObject();
selectAsset_twoColumns();
selectAsset_oneColumn();
openFolder_unlocked();
openFolder_locked();
item.lastSelectTime_ticks = data.curPage.lastItemSelectTime_ticks = System.DateTime.UtcNow.Ticks;
}
static void TryOpenFolderInProjectBrowser(EditorWindow browser, int folderInstanceId)
{
if (TryInvoke(browser, "ShowFolderContents", folderInstanceId, true)) return;
t_BrowserWindow.SetFieldValue("s_LastInteractedProjectBrowser", browser, exceptionIfNotFound: false);
if (TryInvoke(browser, "SetFolderSelection", new[] { folderInstanceId }, false))
if (TryInvoke(t_BrowserWindow, "OpenSelectedFolders")) return;
var listAreaState = browser.GetFieldValue("m_ListAreaState", exceptionIfNotFound: false);
if (TrySetSelectedInstanceId(listAreaState, folderInstanceId))
if (TryInvoke(t_BrowserWindow, "OpenSelectedFolders")) return;
var folderAsset = EditorUtility.InstanceIDToObject(folderInstanceId);
if (folderAsset && AssetDatabase.OpenAsset(folderAsset)) return;
if (TryInvoke(browser, "FrameObjectPrivate", folderInstanceId, true, false)) return;
if (TryInvoke(browser, "FrameObject", folderInstanceId, true)) return;
if (folderAsset) EditorGUIUtility.PingObject(folderAsset);
}
static bool TryInvoke(object target, string methodName, params object[] parameters)
{
if (target == null) return false;
var type = (target as Type) ?? target.GetType();
foreach (var methodInfo in GetMethods(type, methodName, parameters.Length))
{
if (!TryConvertParameters(methodInfo, parameters, out var convertedParameters)) continue;
try
{
methodInfo.Invoke(target is Type ? null : target, convertedParameters);
return true;
}
catch { }
}
return false;
}
static bool TrySetSelectedInstanceId(object listAreaState, int folderInstanceId)
{
if (listAreaState == null) return false;
var fieldInfo = listAreaState.GetType().GetField("m_SelectedInstanceIDs", maxBindingFlags);
if (fieldInfo == null) return false;
var listType = fieldInfo.FieldType;
var itemType = listType.IsGenericType ? listType.GetGenericArguments()[0] : typeof(int);
if (!TryConvertValue(folderInstanceId, itemType, out var convertedInstanceId)) return false;
if (!(System.Activator.CreateInstance(typeof(List<>).MakeGenericType(itemType)) is IList selectedIds)) return false;
selectedIds.Add(convertedInstanceId);
try
{
fieldInfo.SetValue(listAreaState, selectedIds);
return true;
}
catch { return false; }
}
static bool TryConvertParameters(MethodInfo methodInfo, object[] parameters, out object[] convertedParameters)
{
var parameterInfos = methodInfo.GetParameters();
convertedParameters = new object[parameters.Length];
for (var i = 0; i < parameters.Length; i++)
if (TryConvertValue(parameters[i], parameterInfos[i].ParameterType, out var convertedParameter))
convertedParameters[i] = convertedParameter;
else
return false;
return true;
}
static bool TryConvertValue(object value, Type targetType, out object convertedValue)
{
convertedValue = null;
if (value == null)
return !targetType.IsValueType;
var sourceType = value.GetType();
if (targetType.IsAssignableFrom(sourceType))
{
convertedValue = value;
return true;
}
if (targetType.IsArray && value is IEnumerable enumerable)
{
var itemType = targetType.GetElementType();
var convertedItems = new List<object>();
foreach (var item in enumerable)
{
if (!TryConvertValue(item, itemType, out var convertedItem)) return false;
convertedItems.Add(convertedItem);
}
var array = System.Array.CreateInstance(itemType, convertedItems.Count);
for (var i = 0; i < convertedItems.Count; i++)
array.SetValue(convertedItems[i], i);
convertedValue = array;
return true;
}
if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(List<>) && value is IEnumerable listEnumerable)
{
var itemType = targetType.GetGenericArguments()[0];
if (!(System.Activator.CreateInstance(targetType) is IList list)) return false;
foreach (var item in listEnumerable)
{
if (!TryConvertValue(item, itemType, out var convertedItem)) return false;
list.Add(convertedItem);
}
convertedValue = list;
return true;
}
if (value is int instanceId && TryConvertInstanceId(instanceId, targetType, out convertedValue))
return true;
try
{
convertedValue = System.Convert.ChangeType(value, targetType);
return true;
}
catch { return false; }
}
static bool TryConvertInstanceId(int instanceId, Type targetType, out object convertedValue)
{
convertedValue = null;
if (targetType == typeof(int))
{
convertedValue = instanceId;
return true;
}
foreach (var methodInfo in targetType.GetMethods(maxBindingFlags).Where(r => r.IsStatic && (r.Name == "op_Implicit" || r.Name == "op_Explicit")))
{
var parameters = methodInfo.GetParameters();
if (parameters.Length != 1) continue;
if (parameters[0].ParameterType != typeof(int)) continue;
if (methodInfo.ReturnType != targetType) continue;
try
{
convertedValue = methodInfo.Invoke(null, new object[] { instanceId });
return true;
}
catch { }
}
foreach (var constructorInfo in targetType.GetConstructors(maxBindingFlags))
{
var parameters = constructorInfo.GetParameters();
if (parameters.Length != 1) continue;
if (parameters[0].ParameterType != typeof(int)) continue;
try
{
convertedValue = constructorInfo.Invoke(new object[] { instanceId });
return true;
}
catch { }
}
try
{
var value = System.Activator.CreateInstance(targetType);
var intField = targetType.GetFields(maxBindingFlags).FirstOrDefault(r => r.FieldType == typeof(int) || r.FieldType == typeof(long));
if (intField == null) return false;
intField.SetValue(value, intField.FieldType == typeof(long) ? (object)(long)instanceId : instanceId);
convertedValue = value;
return true;
}
catch { return false; }
}
static IEnumerable<MethodInfo> GetMethods(Type type, string methodName, int argumentCount)
{
IEnumerable<MethodInfo> getMethods(Type curType)
{
foreach (var methodInfo in curType.GetMethods(maxBindingFlags))
{
if (methodInfo.Name != methodName) continue;
var parameters = methodInfo.GetParameters();
if (parameters.Length == argumentCount)
yield return methodInfo;
}
}
for (var curType = type; curType != null; curType = curType.BaseType)
foreach (var methodInfo in getMethods(curType))
yield return methodInfo;
foreach (var interfaceType in type.GetInterfaces())
foreach (var methodInfo in getMethods(interfaceType))
yield return methodInfo;
}
static void OpenItem(Item item)
{
void openText()
{
if (item.assetPath.GetExtension() != ".cs"
&& item.assetPath.GetExtension() != ".shader"
&& item.assetPath.GetExtension() != ".compute"
&& item.assetPath.GetExtension() != ".cginc"
&& item.assetPath.GetExtension() != ".json") return;
AssetDatabase.OpenAsset(item.globalId.guid.LoadGuid());
}
void openPrefab()
{
if (item.type != typeof(GameObject)) return;
if (!item.isLoadable) return;
if ((item.obj as GameObject).scene.rootCount != 0) return;
AssetDatabase.OpenAsset(item.obj);
}
void openScene()
{
if (item.type != typeof(SceneAsset)) return;
EditorSceneManager.SaveOpenScenes();
EditorSceneManager.OpenScene(item.assetPath);
}
void openSceneForNotLoadedGameObject()
{
if (!item.isSceneGameObject) return;
if (item.isLoadable) return;
if (item.isDeleted) return;
EditorSceneManager.SaveOpenScenes();
EditorSceneManager.OpenScene(item.assetPath);
Selection.activeObject = item.obj;
}
openText();
openPrefab();
openScene();
openSceneForNotLoadedGameObject();
}
static float rowHeight => 44 * data.rowScale;
static float crossButtonOffsetFromRight = 23;
static float crossButtonSize = 16;
static Rect totalRect_browserSpace => isOneColumn ? wrappedBrowser.position.SetPos(0, 0) : wrappedBrowser.GetFieldValue<Rect>("m_TreeViewRect");
static Rect totalRect_groupSpace => totalRect_browserSpace.SetPos(0, 0);
static Rect widgetRect_browserSpace => widgetRect_groupSpace.MoveY(totalRect_browserSpace.y);
static Rect widgetRect_groupSpace;
static bool renamingPage;
static string prevPageName;
static int lastNumberKeyPressedIndex;
static long lastNumberKeyPressedTime_ticks;
static bool isOneColumn => wrappedBrowser?.GetFieldValue<int>("m_ViewMode") == 0;
static bool skipNextRepaint;
static bool originalGUICalledOnce;
static void UpdateMouseState() // called from WrappedOnGUI
{
if (!shortcutPressed && !isWrappedBrowserLocked) { setDefaultState(); return; }
if (!totalRect_browserSpace.IsHovered()) { setDefaultState(); return; }
void setDefaultState()
{
mousePressed = false;
mousePressedOnCrossButtonArea = false;
mousePressedOnWidget = false;
pressedItem = null;
doubleclickUnhandled = false;
}
void position()
{
mousePosiion_browserSpace = curEvent.mousePosition;
}
void down()
{
if (!curEvent.isMouseDown) return;
mousePressed = true;
mousePressedOnCrossButtonArea = totalRect_browserSpace.SetWidthFromRight(0).MoveX(-crossButtonOffsetFromRight).SetWidthFromMid(crossButtonSize).IsHovered();
mousePressedOnWidget = curEvent.mousePosition.y >= widgetRect_browserSpace.y;
mouseDownPosiion_browserSpace = curEvent.mousePosition;
var pressedItemIndex = (mouseDownPosition_rowsSpace.y / rowHeight).FloorToInt();
if (pressedItemIndex.IsInRangeOf(data.curPage.items) && !mousePressedOnCrossButtonArea && !mousePressedOnWidget)
pressedItem = data.curPage.items[pressedItemIndex];
doubleclickUnhandled = !mousePressedOnCrossButtonArea && curEvent.clickCount == 2;
curEvent.Use();
}
void up()
{
if (!curEvent.isMouseUp) return;
mousePressed = false;
doubleclickUnhandled = false;
pressedItem = null;
}
position();
down();
up();
}
static bool mousePressed;
static bool mousePresesdOnItem => pressedItem != null;
static bool mousePressedOnCrossButtonArea;
static bool mousePressedOnWidget;
static bool doubleclickUnhandled;
static float groupRectOffsetY => isOneColumn ? 0 : -20;
static Vector2 mousePosiion_browserSpace;
static Vector2 mousePosition_groupSpace => mousePosiion_browserSpace.AddY(groupRectOffsetY);
static Vector2 mousePosition_rowsSpace => mousePosiion_browserSpace.AddY(groupRectOffsetY + data.curPage.scrollPos);
static Vector2 mouseDownPosiion_browserSpace;
static Vector2 mouseDownPosition_groupSpace => mouseDownPosiion_browserSpace.AddY(groupRectOffsetY);
static Vector2 mouseDownPosition_rowsSpace => mouseDownPosiion_browserSpace.AddY(groupRectOffsetY + data.curPage.scrollPos);
static float mouseDragDistance => (mousePosiion_browserSpace - mouseDownPosiion_browserSpace).magnitude;
static Item pressedItem;
static void UpdateAnimations() // called from WrappedOnGUI
{
void calcDeltaTime()
{
if (!curEvent.isLayout) return;
deltaTime = (float)(EditorApplication.timeSinceStartup - lastLayoutTime);
if (deltaTime > .05f)
deltaTime = .0166f;
lastLayoutTime = EditorApplication.timeSinceStartup;
}
void opacity()
{
if (!curEvent.isLayout) return;
if (!VFavoritesMenu.fadeAnimationsEnabled) { currentOpacity = targetOpacity; return; }
if (!UnityEditorInternal.InternalEditorUtility.isApplicationActive) { currentOpacity = targetOpacity; return; }
SmoothDamp(ref currentOpacity, targetOpacity, 11, ref currentOpacityDerivative, deltaTime);
if (targetOpacity == 0 && currentOpacity < .04f)
currentOpacity = 0;
}
void pagesScroll()
{
if (!curEvent.isLayout) return;
if (!VFavoritesMenu.pageScrollAnimationEnabled) { pagesScrollPos = data.curPageIndex; return; }
if (pagesScrollPos == -1)
pagesScrollPos = data.curPageIndex;
SmoothDamp(ref pagesScrollPos, data.curPageIndex, 5, ref pagesScrollDerivative, deltaTime);
if (pagesScrollPos.DistanceTo(data.curPageIndex) < .001f)
pagesScrollPos = data.curPageIndex;
}
void rowGaps()
{
if (!curEvent.isLayout) return;
var lerpSpeed = 10;
for (int i = 0; i < data.curPage.rowGaps.Count; i++)
data.curPage.rowGaps[i] = Lerp(data.curPage.rowGaps[i], draggingItem && i == insertDraggedItemAtIndex ? rowHeight : 0, lerpSpeed, deltaTime);
}
void droppedItem()
{
if (!curEvent.isLayout) return;
if (!animatingDroppedItem) return;
var yLerpSpeed = 8;
var shadowLerpSpeed = 8;
var highlightLerpSpeed = 10;
SmoothDamp(ref droppedItemY_rowsSpace, data.curPage.items.IndexOf(VFavorites.droppedItem) * rowHeight, yLerpSpeed, ref droppedItemYDerivative, deltaTime);
Lerp(ref droppedItemShadowAmount, 0, shadowLerpSpeed, deltaTime);
Lerp(ref droppedItemHighlightAmount, 0, highlightLerpSpeed, deltaTime);
if (droppedItemShadowAmount < .01f)
animatingDroppedItem = false;
}
calcDeltaTime();
opacity();
pagesScroll();
rowGaps();
droppedItem();
}
static void CancelRowAnimations()
{
for (int i = 0; i < data.curPage.rowGaps.Count; i++)
data.curPage.rowGaps[i] = 0;
animatingDroppedItem = false;
droppedItem = null;
}
static float deltaTime;
static double lastLayoutTime;
static float pagesScrollPos = -1;
static float pagesScrollDerivative;
static bool animatingPageScroll => !pagesScrollPos.Approx(pagesScrollPos.Round());
static float droppedItemY_rowsSpace;
static float droppedItemYDerivative;
static float droppedItemShadowAmount;
static float droppedItemHighlightAmount;
static bool animatingDroppedItem;
static float currentOpacity;
static float currentOpacityDerivative;
static float targetOpacity => curEvent.holdingAlt || renamingPage || draggingItemFromPageToOutside || isWrappedBrowserLocked ? 1 : 0; // holdingAlt instead of shortcutPressed to prevent unwrapping due to incorrect event modifiers on key down on mac
static void UpdateDragging() // called from WrappedOnGUI
{
void initFromOutside()
{
if (draggingItem) return;
if (!totalRect_browserSpace.IsHovered()) return;
if (!curEvent.isDragUpdate) return;
if (!DragAndDrop.objectReferences.FirstOrDefault()) return;
if (draggingItemFromPageToOutside) return; // to avoid duplication when dragging item from page to outside and back
animatingDroppedItem = false;
draggingItem = true;
draggingItemFromPage = false;
draggedItem = new Item(DragAndDrop.objectReferences.FirstOrDefault());
draggedItemHoldOffset = 0;
data.curPage.lastItemDragTime_ticks = System.DateTime.UtcNow.Ticks;
}
void initFromPage()
{
if (draggingItem) return;
if (!totalRect_browserSpace.IsHovered()) return;
if (!curEvent.isMouseDrag) return;
if (mouseDragDistance < 2) return;
var i = (mouseDownPosition_rowsSpace.y / rowHeight).FloorToInt();
if (i >= data.curPage.items.Count) return;
if (i < 0) return; // somehow i = -1 with mouseDownPosition_rowsSpace.y = -20 when dragging in from outside quickly
animatingDroppedItem = false;
draggingItem = true;
draggingItemFromPage = true;
draggingItemFromPageAtIndex = i;
draggedItem = data.curPage.items[i];
draggedItemHoldOffset = (i * rowHeight + rowHeight / 2) - mouseDownPosition_rowsSpace.y;
data.curPage.lastItemDragTime_ticks = System.DateTime.UtcNow.Ticks;
data.curPage.items.Remove(draggedItem);
data.curPage.rowGaps[draggingItemFromPageAtIndex] = rowHeight;
data.Dirty();
data.Save();
}
void acceptFromOutside()
{
if (!draggingItem) return;
if (!curEvent.isDragPerform) return;
DragAndDrop.AcceptDrag();
curEvent.Use();
AcceptDragging();
}
void acceptFromPage()
{
if (!draggingItem) return;
if (!curEvent.isMouseUp) return;
curEvent.Use();
AcceptDragging();
}
void cancelFromOutside()
{
if (!draggingItemFromOutside) return;
if (totalRect_browserSpace.IsHovered()) return;
CancelDragging();
}
void cancelFromPageAndInitToOutside()
{
if (!curEvent.isMouseDrag) return;
if (!draggingItemFromPage) return;
if (totalRect_browserSpace.IsHovered()) return;
if (DragAndDrop.objectReferences.Any()) return;
DragAndDrop.PrepareStartDrag();
DragAndDrop.objectReferences = new[] { draggedItem.obj };
DragAndDrop.StartDrag(draggedItem.name);
CancelDragging();
draggingItemFromPageToOutside = true;
}
void setVisualMode()
{
if (!draggingItem) return;
DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
}
void setHotControl()
{
if (!draggingItem) return;
EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive);
}
void resetDraggingItemFromPageToOutside() // delayCall loop
{
if (!DragAndDrop.objectReferences.Any())
draggingItemFromPageToOutside = false;
EditorApplication.delayCall -= resetDraggingItemFromPageToOutside;
EditorApplication.delayCall += resetDraggingItemFromPageToOutside;
// DragAndDrop.objectReferences is unreliable outside of delayCall
}
initFromOutside();
initFromPage();
acceptFromOutside();
acceptFromPage();
cancelFromOutside();
cancelFromPageAndInitToOutside();
setVisualMode();
setHotControl();
EditorApplication.delayCall -= resetDraggingItemFromPageToOutside;
EditorApplication.delayCall += resetDraggingItemFromPageToOutside;
}
static void AcceptDragging()
{
draggingItem = false;
draggingItemFromPage = false;
mousePressed = false;
data.curPage.items.AddAt(draggedItem, insertDraggedItemAtIndex);
data.curPage.rowGaps[insertDraggedItemAtIndex] -= rowHeight;
data.curPage.rowGaps.AddAt(0, insertDraggedItemAtIndex);
droppedItem = draggedItem;
droppedItemY_rowsSpace = draggedItemY_groupSpace + data.curPage.scrollPos;
droppedItemYDerivative = 0;
droppedItemShadowAmount = droppedItemHighlightAmount = 1;
animatingDroppedItem = true;
draggedItem = null;
EditorGUIUtility.hotControl = 0;
data.Dirty();
data.Save();
}
static void CancelDragging()
{
if (!draggingItem) return;
draggingItem = false;
mousePressed = false;
if (!draggingItemFromPage) { draggedItem = null; return; }
data.curPage.items.AddAt(draggedItem, draggingItemFromPageAtIndex);
data.curPage.rowGaps[draggingItemFromPageAtIndex] -= rowHeight;
droppedItem = draggedItem;
droppedItemY_rowsSpace = draggedItemY_groupSpace - data.curPage.scrollPos;
droppedItemShadowAmount = droppedItemHighlightAmount = 1;
animatingDroppedItem = true;
draggingItemFromPage = false;
draggedItem = null;
EditorGUIUtility.hotControl = 0;
data.Dirty();
data.Save();
}
static bool draggingItem;
static bool draggingItemFromPage;
static bool draggingItemFromPageToOutside;
static bool draggingItemFromOutside => draggingItem && !draggingItemFromPage;
static int draggingItemFromPageAtIndex;
static Item draggedItem;
static float draggedItemHoldOffset;
static float draggedItemY_groupSpace => (mousePosition_groupSpace.y - rowHeight / 2 + draggedItemHoldOffset).Clamp(0, 12321);
static float draggedItemY_rowsSpace => draggedItemY_groupSpace + data.curPage.scrollPos;
static int insertDraggedItemAtIndex => ((mousePosition_rowsSpace.y + draggedItemHoldOffset) / rowHeight).FloorToInt().Clamp(0, data.curPage.items.Count);
static Item droppedItem;
static void UpdateGUIWrapping() // called from EditorApplicaton.update
{
void wrap()
{
if (wrappedBrowser) return;
if (!UnityEditorInternal.InternalEditorUtility.isApplicationActive) return;
if (!shortcutPressed) return;
if (EditorWindow.mouseOverWindow?.GetType() != t_BrowserWindow) return;
if (t_VTabs != null && !EditorPrefs.GetBool("vFavorites-pluginDisabled", false) && EditorWindow.mouseOverWindow.GetMemberValue<bool>("isLocked")) return;
WrapBrowserGUI(EditorWindow.mouseOverWindow);
wrappedBrowser = EditorWindow.mouseOverWindow;
wrappedBrowser.Focus();
wrappedBrowser.Repaint();
t_BrowserWindow.SetFieldValue("s_LastInteractedProjectBrowser", wrappedBrowser); // so vTabs can copy its layout setting
}
void unwrap()
{
if (!wrappedBrowser) return;
if (shortcutPressed && wrappedBrowser.hasFocus) return;
if (currentOpacity > 0 && wrappedBrowser.hasFocus) return;
CancelDragging();
CancelRowAnimations();
UnwrapBrowserGUI();
wrappedBrowser.Repaint();
wrappedBrowser = null;
}
unwrap();
wrap();
}
static void WrapBrowserGUI(EditorWindow browser)
{
var hostView = fi_m_Parent.GetValue(browser);
var newDelegate = mi_OnGUIOverride.CreateDelegate(t_EditorWindowDelegate, hostView);
origBrowserOnGUIDelegate = fi_m_OnGUI.GetValue(hostView) as System.Delegate;
fi_m_OnGUI.SetValue(hostView, newDelegate);
}
static void UnwrapBrowserGUI()
{
if (wrappedBrowser.GetFieldValue("m_Parent").GetFieldValue<System.Delegate>("m_OnGUI").Method.Name != nameof(WrappedOnGUI)) return;
wrappedBrowser.GetFieldValue("m_Parent").SetFieldValue("m_OnGUI", origBrowserOnGUIDelegate);
}
static EditorWindow wrappedBrowser;
static System.Delegate origBrowserOnGUIDelegate;
static bool shortcutPressed
{
get
{
if (VFavoritesMenu.activeOnAltEnabled)
return curEvent.holdingAlt;
if (VFavoritesMenu.activeOnAltShiftEnabled)
return curEvent.modifiers == (EventModifiers.Alt | EventModifiers.Shift);
if (VFavoritesMenu.activeOnCtrlAltEnabled)
if (Application.platform == RuntimePlatform.OSXEditor)
return curEvent.modifiers == (EventModifiers.Command | EventModifiers.Alt);
else
return curEvent.modifiers == (EventModifiers.Control | EventModifiers.Alt);
return false;
}
}
static void UpdateLocking() // called from EditorApplicaton.update
{
void unsetWrappedBrowser()
{
if (!isWrappedBrowserLocked) return;
if (wrappedBrowser.GetFieldValue("m_Parent").GetFieldValue<System.Delegate>("m_OnGUI").Method.Name == nameof(WrappedOnGUI)) return;
wrappedBrowser = null;
// nulls wrappedBrowser if it was unwrapped externally
// ie when locked browser gets moved or maximized
}
void markLockedBrowser()
{
if (!lockedBrowser) return;
MarkAsLocked(lockedBrowser);
// fixes marking getting reset on scene load
}
void setMinWidthOnLockedBrowser()
{
if (!lockedBrowser) return;
if (lockedBrowser.minSize.x == 100) return;
lockedBrowser.minSize = Vector2.one * 100;
}
void lock_()
{
if (lockedBrowser) return;
if (!wrappedBrowser) return;
if (!wrappedBrowser.GetMemberValue<bool>("isLocked")) return;
lockedBrowser = wrappedBrowser;
EditorPrefs.SetInt("vFavorites-lockedBrowserHash", lockedBrowser.GetHashCode());
EditorPrefs.SetInt("vFavorites-lockedBrowserDockAreaInstanceId", lockedBrowser.GetMemberValue<Object>("m_Parent").GetInstanceID());
curEvent.Use();
}
void unlock()
{
if (!lockedBrowser) return;
if (lockedBrowser.GetMemberValue<bool>("isLocked")) return;
lockedBrowser = null;
EditorPrefs.SetInt("vFavorites-lockedBrowserHash", 0);
EditorPrefs.SetInt("vFavorites-lockedBrowserDockAreaInstanceId", 0);
curEvent.Use();
}
void wrap()
{
if (!lockedBrowser) return;
if (!lockedBrowser.hasFocus) return;
if (isWrappedBrowserLocked) return;
WrapBrowserGUI(lockedBrowser);
currentOpacity = 1;
wrappedBrowser = lockedBrowser;
}
unsetWrappedBrowser();
markLockedBrowser();
setMinWidthOnLockedBrowser();
lock_();
unlock();
wrap();
}
static EditorWindow lockedBrowser
{
get
{
if (_lockedBrowser) return _lockedBrowser;
var lockedBrowserInstanceId = EditorPrefs.GetInt("vFavorites-lockedBrowserInstanceId", 0);
if (lockedBrowserInstanceId == 0) return null;
var window = Resources.InstanceIDToObject(lockedBrowserInstanceId) as EditorWindow;
if (window && window.GetType() == t_BrowserWindow) // prevents iid collisions
_lockedBrowser = window;
if (_lockedBrowser) return _lockedBrowser;
_lockedBrowser = allBrowsers.FirstOrDefault(r => IsMarkedAsLocked(r));
if (_lockedBrowser) return _lockedBrowser; // todo set instanceid
// EditorPrefs.SetInt("vFavorites-lockedBrowserInstanceId", 0); // one attempt to find the locked browser isn't enough after unmaximize
return null;
}
set
{
if (_lockedBrowser)
MarkAsUnlocked(_lockedBrowser);
if (value == null)
{
EditorPrefs.SetInt("vFavorites-lockedBrowserInstanceId", 0);
_lockedBrowser = null;
}
else
{
EditorPrefs.SetInt("vFavorites-lockedBrowserInstanceId", value.GetInstanceID());
MarkAsLocked(value);
_lockedBrowser = value;
}
}
}
static EditorWindow _lockedBrowser;
static bool IsMarkedAsLocked(EditorWindow browser) => browser.GetMemberValue("m_SearchFilter")?.GetMemberValue<string>("m_OriginalText") == "asd";
static void MarkAsLocked(EditorWindow browser) => browser.GetMemberValue("m_SearchFilter")?.SetMemberValue("m_OriginalText", "asd");
static void MarkAsUnlocked(EditorWindow browser) => browser.GetMemberValue("m_SearchFilter")?.SetMemberValue("m_OriginalText", "");
static bool isWrappedBrowserLocked => wrappedBrowser && wrappedBrowser == lockedBrowser;
static bool CanBrowserBeWrapped_byVTabs(EditorWindow browser) => !IsMarkedAsLocked(browser);
[InitializeOnLoadMethod]
static void Init()
{
if (VFavoritesMenu.pluginDisabled) return;
void subscribe()
{
EditorApplication.update -= UpdateGUIWrapping;
EditorApplication.update += UpdateGUIWrapping;
EditorApplication.update -= UpdateLocking;
EditorApplication.update += UpdateLocking;
EditorApplication.quitting -= VFavoritesState.Save;
EditorApplication.quitting += VFavoritesState.Save;
}
void loadData()
{
data = AssetDatabase.LoadAssetAtPath<VFavoritesData>(EditorPrefs.GetString("vFavorites-lastKnownDataPath-" + GetProjectId()));
if (data) return;
data = AssetDatabase.FindAssets("t:VFavoritesData").Select(guid => AssetDatabase.LoadAssetAtPath<VFavoritesData>(guid.ToPath())).FirstOrDefault();
if (!data) return;
EditorPrefs.SetString("vFavorites-lastKnownDataPath-" + GetProjectId(), data.GetPath());
}
void loadDataDelayed()
{
if (data) return;
EditorApplication.delayCall += () => EditorApplication.delayCall += loadData;
// AssetDatabase isn't up to date at this point (it gets updated after InitializeOnLoadMethod)
// and if current AssetDatabase state doesn't contain the data - it won't be loaded during Init()
// so here we schedule an additional, delayed attempt to load the data
// this addresses reports of data loss when trying to load it on a new machine
}
subscribe();
loadData();
loadDataDelayed();
}
public static VFavoritesData data;
static void BeforeWindowCreated_byVTabs(object dockArea)
{
if (!wrappedBrowser) return;
if (wrappedBrowser.GetFieldValue("m_Parent") != dockArea) return;
CancelDragging();
CancelRowAnimations();
UnwrapBrowserGUI();
draggingItemFromPageToOutside = false;
}
static IEnumerable<EditorWindow> allBrowsers => _allBrowsers ??= t_BrowserWindow.GetFieldValue<IList>("s_ProjectBrowsers").Cast<EditorWindow>();
static IEnumerable<EditorWindow> _allBrowsers;
static Type t_BrowserWindow = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser");
static Type t_HostView = typeof(Editor).Assembly.GetType("UnityEditor.HostView");
static Type t_EditorWindowDelegate = t_HostView.GetNestedType("EditorWindowDelegate", maxBindingFlags);
static FieldInfo fi_m_Parent = typeof(EditorWindow).GetField("m_Parent", maxBindingFlags);
static FieldInfo fi_m_OnGUI = t_HostView.GetField("m_OnGUI", maxBindingFlags);
static MethodInfo mi_OnGUIOverride = typeof(VFavorites).GetMethod(nameof(WrappedOnGUI), 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_VTabs = Type.GetType("VTabs.VTabs") ?? Type.GetType("VTabs.VTabs, VTabs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
static MethodInfo mi_VHierarchy_GetIconName = t_VHierarchy?.GetMethod("GetIconName_forVFavorites", maxBindingFlags);
static MethodInfo mi_VFolders_DrawBigFolderIcon = t_VFolders?.GetMethod("DrawBigFolderIcon_forVFavorites", maxBindingFlags);
const string version = "2.0.6";
}
}
#endif