unity-vendor-packages/vfolders2/vFolders/VFoldersGUI.cs

1279 lines
42 KiB
C#
Raw Normal View History

2025-08-18 09:22:24 +08:00
#if UNITY_EDITOR
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEditor;
using UnityEditor.ShortcutManagement;
using UnityEngine.UIElements;
using UnityEngine.SceneManagement;
using UnityEditor.SceneManagement;
using UnityEditor.IMGUI.Controls;
using Type = System.Type;
using static VFolders.Libs.VUtils;
using static VFolders.Libs.VGUI;
// using static VTools.VDebug;
using static VFolders.VFolders;
using static VFolders.VFoldersData;
using static VFolders.VFoldersCache;
namespace VFolders
{
public class VFoldersGUI
{
public void RowGUI(Rect rowRect, string guid, int instanceId)
{
var fullRowRect = rowRect.AddWidthFromRight(rowRect.x);
var isRowHovered = fullRowRect.IsHovered();
var isListArea = rowRect.x == 14;
var isFolder = AssetDatabase.IsValidFolder(guid.ToPath());
var isAsset = !isFolder && !guid.IsNullOrEmpty();
var isFavorite = !isFolder && !isAsset && rowRect.x != 16;
var isFavoritesRoot = rowRect.x == 16 && !isFolder && rowRect.y == 0;
var isAssetsRoot = rowRect.x == 16 && isFolder && guid.ToPath() == "Assets";
var isPackagesRoot = rowRect.x == 16 && !isFavoritesRoot && !isAssetsRoot && guid.IsNullOrEmpty();
var useMinimalMode = VFoldersMenu.minimalModeEnabled && !isListArea;
var useBackgroundColors = (VFoldersMenu.backgroundColorsEnabled || useMinimalMode) && !isListArea;
var useHierarchyLines = VFoldersMenu.hierarchyLinesEnabled && !isListArea;
var useContentMinimap = VFoldersMenu.contentMinimapEnabled;
var useZebraStriping = VFoldersMenu.zebraStripingEnabled;
TreeViewItem treeItem = null;
2025-08-18 09:22:24 +08:00
var isRowSelected = false;
var isRowBeingRenamed = false;
void setObjects()
{
if (isListArea) return;
if (!curEvent.isRepaint) // only needed for drawing,
if (!curEvent.isMouseUp) // altClick,
if (!isRowHovered) // and setting last hovered tree item
return;
void set_treeItem_byRect()
{
if (treeViewAnimatesExpansion) return;
var offest = isTwoColumns ? -15 : -4;
if ((rowRect.y + offest) % 16 != 0) return;
var rowIndex = ((rowRect.y + offest) / 16).ToInt();
if (rowIndex < 0 || rowIndex >= rows.Count) return;
if (rows == null) return;
treeItem = rows[rowIndex];
}
void set_treeItem_byInstanceId()
{
if (treeItem != null) return;
if (isFavorite || isFavoritesRoot) return;
treeItem = treeViewController?.InvokeMethod<TreeViewItem>("FindItem", instanceId);
2025-08-18 09:22:24 +08:00
}
set_treeItem_byRect();
set_treeItem_byInstanceId();
}
void setState()
{
void set_isRowSelected_oneColumn()
{
if (!curEvent.isRepaint) return;
if (isListArea) return;
if (treeItem == null) return;
if (isTwoColumns) return;
var dragging = treeView_dragSelectionList?.Any() == true;
isRowSelected = dragging ? (treeView_dragSelectionList.Contains(treeItem.id)) : Selection.Contains(treeItem.id);
}
void set_isRowSelected_twoColumns()
{
if (!curEvent.isRepaint) return;
if (isListArea) return;
if (treeItem == null) return;
if (!isTwoColumns) return;
var dragging = treeView_dragSelectionList != null
&& treeView_dragSelectionList.Any();
isRowSelected = dragging ? (treeView_dragSelectionList.Contains(treeItem.id)) : treeView_normalSelectionList != null && treeView_normalSelectionList.Contains(treeItem.id);
}
void set_lastHovered()
{
if (isListArea) return;
if (!isRowHovered) return;
lastHoveredRowRect_screenSpace = EditorGUIUtility.GUIToScreenRect(fullRowRect);
lastHoveredTreeItem = treeItem;
}
void set_lastKnownMousePosition()
{
if (!curEvent.isRepaint) return;
lastKnownMousePosition_screenSpace = curEvent.mousePosition_screenSpace;
}
set_isRowSelected_oneColumn();
set_isRowSelected_twoColumns();
set_lastHovered();
set_lastKnownMousePosition();
isRowBeingRenamed = renamingRow && isRowSelected;
}
void drawing()
{
if (!curEvent.isRepaint) { hierarchyLines_isFirstRowDrawn = false; return; }
var folderInfo = GetFolderInfo(guid);
var rowHasColor = useBackgroundColors && folderInfo.hasColor;
var rowHasCustomIcon = !isFolder ? false
: useBackgroundColors ? folderInfo.hasIcon
: folderInfo.hasIcon || folderInfo.hasColor;
var rowHasIcon = !useMinimalMode ? true
: rowHasCustomIcon || isAsset || isFavorite || isRowBeingRenamed;
var hideName = rowHasColor || !rowHasIcon;
var hideDefaultIcon = rowHasCustomIcon || !rowHasIcon;
var drawTriangle = rowHasColor && treeItem?.hasChildren == true;
var drawName = rowHasColor || !rowHasIcon;
var drawDefaultIcon = (rowHasColor && !rowHasCustomIcon) && rowHasIcon;
var makeTriangleBrighter = rowHasColor && isDarkTheme;
var makeNameBrighter = rowHasColor && isDarkTheme;
var makeIconBrighter = rowHasColor && isFolder;
var defaultBackground = isListArea ? Greyscale(isDarkTheme ? .2f : .75f)
: GUIColors.windowBackground;
if (isRowSelected)
if (!isRowBeingRenamed)
defaultBackground = isTreeFocused ? GUIColors.selectedBackground
: Greyscale(isDarkTheme ? .3f : .68f);
void hideName_()
{
if (!hideName) return;
if (isFavorite) return;
var name = isListArea ? guid.ToPath().GetFilename() : treeItem != null ? treeItem.displayName : "Favorites";
var nameRect = rowRect.SetWidth(name.GetLabelWidth() + 3).MoveX(16).MoveX(isListArea ? 3 : 0);
nameRect.Draw(defaultBackground);
}
void hideDefaultIcon_()
{
if (!hideDefaultIcon) return;
var iconRect = rowRect.SetWidth(16).MoveX(isListArea ? 3 : 0);
if (isListArea)
{
SetGUIColor(defaultBackground);
GUI.DrawTexture(iconRect, EditorIcons.GetIcon("Folder On Icon"));
ResetGUIColor();
}
else
iconRect.Draw(defaultBackground);
}
void color()
{
if (!rowHasColor) return;
var color = (isDarkTheme ? Color.Lerp(folderInfo.color, Greyscale(.05f), .42f)
: Color.Lerp(folderInfo.color, Greyscale(.8f), .5f)).SetAlpha(1);
if (isRowHovered)
color *= isDarkTheme ? 1.1f : .92f;
if (isRowSelected)
color *= isDarkTheme ? 1.2f : .8f;
if (palette?.colorGradientsEnabled == false)
color = MathUtil.Lerp(color, Greyscale(.2f), isDarkTheme ? .25f : .03f);
if (folderInfo.hasColorByRecursion)
color = MathUtil.Lerp(color, Greyscale(isDarkTheme ? .24f : .8f), .45f);
var colorRect = rowRect.AddWidthFromRight(30).AddWidth(16);
if (folderInfo.hasColorByRecursion)
colorRect = colorRect.AddWidthFromRight(folderInfo.maxColorRecursionDepth * 14);
if (!isRowSelected && !folderInfo.hasColorByRecursion)
colorRect = colorRect.AddHeightFromMid(EditorGUIUtility.pixelsPerPoint >= 2 ? -.5f : -1);
if (folderInfo.hasColorByRecursion)
colorRect = colorRect.MoveY(EditorGUIUtility.pixelsPerPoint >= 2 ? -.25f : -.5f);
if (palette?.colorGradientsEnabled == false) { colorRect.Draw(color); return; }
var hasLeftGradient = colorRect.x > 4;
if (hasLeftGradient)
colorRect = colorRect.AddWidthFromRight(3);
var leftGradientWith = hasLeftGradient ? 22 : 0;
var rightGradientWidth = (fullRowRect.width * .77f).Min(colorRect.width - leftGradientWith);
var leftGradientRect = colorRect.SetWidth(leftGradientWith);
var rightGradientRect = colorRect.SetWidthFromRight(rightGradientWidth);
var flatColorRect = colorRect.SetX(leftGradientRect.xMax).SetXMax(rightGradientRect.x);
leftGradientRect.AddWidth(1).DrawCurtainLeft(color);
flatColorRect.AddWidth(1).Draw(color);
rightGradientRect.Draw(color.MultiplyAlpha(.1f));
rightGradientRect.DrawCurtainRight(color);
}
void triangle()
{
if (!drawTriangle) return;
var triangleRect = rowRect.MoveX(-15).SetWidth(16).Resize(-1);
GUI.Label(triangleRect, EditorGUIUtility.IconContent(expandedIds.Contains(treeItem.id) ? "IN_foldout_on" : "IN_foldout"));
if (!makeTriangleBrighter) return;
GUI.Label(triangleRect, EditorGUIUtility.IconContent(expandedIds.Contains(treeItem.id) ? "IN_foldout_on" : "IN_foldout"));
}
void name()
{
if (!drawName) return;
if (isRowBeingRenamed) return;
var nameRect = rowRect.MoveX(18).AddHeight(1); ;
if (isListArea)
nameRect = nameRect.MoveX(3);
if (!rowHasIcon)
nameRect = nameRect.MoveX(-17);
if (makeNameBrighter)
nameRect = nameRect.MoveX(-2).MoveY(-.5f);
var styleName = makeNameBrighter ? "WhiteLabel" : "TV Line";
if (isFavoritesRoot || isAssetsRoot || isPackagesRoot)
styleName = "BoldLabel";
var name = isFavoritesRoot ? "Favorites" :
isPackagesRoot ? "Packages" :
isListArea || treeItem == null ? guid.ToPath().GetFilename() :
treeItem.displayName;
if (makeNameBrighter)
SetGUIColor(Greyscale(isRowSelected ? 1 : .93f));
GUI.skin.GetStyle(styleName).Draw(nameRect, name, false, false, isRowSelected, isTreeFocused && styleName != "BoldLabel");
if (makeNameBrighter)
ResetGUIColor();
}
void defaultIcon()
{
if (!drawDefaultIcon) return;
var iconRect = rowRect.SetWidth(16).MoveX(isListArea ? 3 : 0);
var icon = isAsset ? AssetDatabase.GetCachedIcon(guid.ToPath())
: makeIconBrighter ? EditorIcons.GetIcon(folderInfo.folderState.isEmpty ? "FolderEmpty On Icon" : "Folder On Icon")
: EditorIcons.GetIcon(folderInfo.folderState.isEmpty ? "FolderEmpty Icon" : "Folder Icon");
SetLabelAlignmentCenter();
if (makeIconBrighter)
SetGUIColor(Greyscale(.88f));
GUI.DrawTexture(iconRect, icon);
if (makeIconBrighter)
ResetGUIColor();
ResetLabelStyle();
}
void customIcon()
{
if (!rowHasCustomIcon) return;
var icon = GetSmallFolderIcon(folderInfo, removeColor: useBackgroundColors);
if (useMinimalMode && folderInfo.hasIcon)
icon = EditorIcons.GetIcon(folderInfo.iconNameOrPath);
if (!icon) return;
var iconRect = rowRect.SetWidth(16).MoveX(isListArea ? 3 : 0);
iconRect = iconRect.SetWidth(iconRect.height / icon.height * icon.width);
GUI.DrawTexture(iconRect, icon);
}
void hierarchyLines()
{
if (!useHierarchyLines) return;
if (isListArea) return;
if (treeItem == null) return;
var lineThickness = 1f;
var lineColor = isDarkTheme ? Greyscale(1, .165f) : Greyscale(0, .23f);
var depth = ((rowRect.x - 16) / 14).RoundToInt();
bool isLastChild(TreeViewItem item) => item.parent?.children?.LastOrDefault() == item;
bool hasChilren(TreeViewItem item) => item.children != null && item.children.Count > 0;
2025-08-18 09:22:24 +08:00
void calcVerticalGaps_beforeFirstRowDrawn()
{
if (hierarchyLines_isFirstRowDrawn) return;
hierarchyLines_verticalGaps.Clear();
var curItem = treeItem.parent;
var curDepth = depth - 1;
while (curItem != null && curItem.parent != null)
{
if (isLastChild(curItem))
hierarchyLines_verticalGaps.Add(curDepth - 1);
curItem = curItem.parent;
curDepth--;
}
}
void updateVerticalGaps_beforeNextRowDrawn()
{
if (isLastChild(treeItem))
hierarchyLines_verticalGaps.Add(depth - 1);
if (depth < hierarchyLines_prevRowDepth)
hierarchyLines_verticalGaps.RemoveAll(r => r >= depth);
}
void drawVerticals()
{
for (int i = 1; i < depth; i++)
if (!hierarchyLines_verticalGaps.Contains(i))
rowRect.SetX(9 + i * 14 - lineThickness / 2)
.SetWidth(lineThickness)
.SetHeight(isLastChild(treeItem) && i == depth - 1 ? 8 + lineThickness / 2 : 16)
.Draw(lineColor);
}
void drawHorizontals()
{
if (depth == 0) return;
if (depth == 1) return;
rowRect.MoveX(-21)
.SetHeightFromMid(lineThickness)
.SetWidth(hasChilren(treeItem) ? 7 : 17)
.AddWidthFromRight(-lineThickness / 2f)
.Draw(lineColor);
}
calcVerticalGaps_beforeFirstRowDrawn();
drawVerticals();
drawHorizontals();
updateVerticalGaps_beforeNextRowDrawn();
hierarchyLines_prevRowDepth = depth;
hierarchyLines_isFirstRowDrawn = true;
}
void zebraStriping_()
{
if (!useZebraStriping) return;
var contrast = isDarkTheme ? .033f : .05f;
var firstRowY = isOneColumn ? 4 : -1;
var t = 1 - (rowRect.y - firstRowY).PingPong(16f) / 16f;
if (isRowHovered || isRowSelected)
if (!VFoldersPaletteWindow.instance || VFoldersPaletteWindow.instance.guids.Contains(guid))
t = 1;
if (t.Approx(0)) return;
fullRowRect.Draw(Greyscale(isDarkTheme ? 1 : 0, contrast * t));
}
void highlight_()
{
if (!controller.animatingHighlight) return;
if (guid.ToPath() != controller.folderToHighlight) return;
var highlightBrightness = isDarkTheme ? .16f : .35f;
var highlightAmount = controller.highlightAmount.Clamp01();
highlightAmount = highlightAmount * highlightAmount * (3 - 2 * highlightAmount);
fullRowRect.AddWidthFromRight(123).Draw(Greyscale(1, highlightBrightness * highlightAmount));
}
void hoverHighlight()
{
if (!VFoldersPaletteWindow.instance)
if (!rowRect.IsHovered()) return;
if (VFoldersPaletteWindow.instance)
if (VFoldersPaletteWindow.instance.guids.Count > 1 || VFoldersPaletteWindow.instance.guids.First() != guid || !paletteOpenedOnRow) return;
fullRowRect.Draw(Greyscale(isDarkTheme ? 1 : 0, .06f));
}
void contentMinimap()
{
if (!isFolder) return;
if (!useContentMinimap) return;
if (guid.IsNullOrEmpty()) return;
void icon(Rect rect, string name)
{
var icon = EditorIcons.GetIcon(name);
if (!icon) return;
SetGUIColor(Greyscale(1, isDarkTheme ? .5f : .7f));
GUI.DrawTexture(rect, icon);
ResetGUIColor();
}
var iconDistance = 13;
var minButtonX = rowRect.x + guid.ToPath().GetFilename().GetLabelWidth() + iconDistance + 2;
var iconRect = fullRowRect.SetWidthFromRight(iconDistance).SetSizeFromMid(12, 12).MoveX(-1.5f);
foreach (var iconName in folderInfo.folderState.contentMinimapIconNames)
{
if (iconRect.x < minButtonX) continue;
icon(iconRect, iconName);
iconRect = iconRect.MoveX(-iconDistance);
}
}
fullRowRect.MarkInteractive();
hideName_();
hideDefaultIcon_();
color();
triangle();
defaultIcon();
customIcon();
name();
hierarchyLines();
zebraStriping_();
highlight_();
hoverHighlight();
contentMinimap();
}
void altDrag()
{
if (!curEvent.holdingAlt) return;
void mouseDown()
{
if (!curEvent.isMouseDown) return;
if (!isRowHovered) return;
mouseDownPos = curEvent.mousePosition;
}
void mouseDrag()
{
if (!curEvent.isMouseDrag) return;
if ((curEvent.mousePosition - mouseDownPos).magnitude < 5) return;
if (!rowRect.Contains(mouseDownPos)) return;
if (!rowRect.Contains(curEvent.mousePosition - curEvent.mouseDelta)) return;
if (DragAndDrop.objectReferences.Any()) return;
DragAndDrop.PrepareStartDrag();
DragAndDrop.objectReferences = new[] { AssetDatabase.LoadAssetAtPath<Object>(guid.ToPath()) };
DragAndDrop.StartDrag(guid.ToPath().GetFilename());
}
mouseDown();
mouseDrag();
// altdrag has to be set up manually before altClick
// because altClick will use() mouseDown event to prevent selection change
}
void altClick()
{
if (!isRowHovered) return;
if (!curEvent.holdingAlt) return;
if (!isFolder) return;
void mouseDown()
{
if (!curEvent.isMouseDown) return;
curEvent.Use();
}
void mouseUp()
{
if (!curEvent.isMouseUp) return;
var selectedGuids = isListArea
?
Selection.objects.Where(r => r is DefaultAsset).Select(r => r.GetPath().ToGuid())
:
#if UNITY_2021_1_OR_NEWER
treeViewController.GetFieldValue("m_CachedSelection").GetFieldValue<List<int>>("m_List")
#else
treeViewController?.GetMemberValue("state").GetMemberValue<List<int>>("selectedIDs")
#endif
.Select(id => treeViewController.InvokeMethod("FindItem", id))
.Where(r => r?.GetType().Name == "FolderTreeItem")
.Select(r => r.GetPropertyValue<string>("Guid"))
.Where(r => r != null);
var editMultiSelection = selectedGuids.Count() > 1 && selectedGuids.Contains(guid);
var guidsToEdit = (editMultiSelection ? selectedGuids.Where(r => AssetDatabase.IsValidFolder(r.ToPath())) : new[] { guid }).ToList();
if (VFoldersPaletteWindow.instance && VFoldersPaletteWindow.instance.guids.SequenceEqual(guidsToEdit)) { VFoldersPaletteWindow.instance.Close(); return; }
var openNearRect = rowRect;
var position = EditorGUIUtility.GUIToScreenPoint(new Vector2(curEvent.mousePosition.x + 20, openNearRect.y - 13));
if (!VFoldersPaletteWindow.instance)
VFoldersPaletteWindow.CreateInstance(position);
VFoldersPaletteWindow.instance.Init(guidsToEdit);
VFoldersPaletteWindow.instance.Focus();
VFoldersPaletteWindow.instance.targetPosition = position;
paletteOpenedOnRow = true;
paletteOpenedOnCell = false;
if (editMultiSelection)
Selection.objects = null;
}
mouseDown();
mouseUp();
}
setObjects();
setState();
drawing();
altDrag();
altClick();
}
List<int> hierarchyLines_verticalGaps = new();
bool hierarchyLines_isFirstRowDrawn;
int hierarchyLines_prevRowDepth;
Vector2 mouseDownPos;
bool paletteOpenedOnRow;
public void CellGUI(Rect cellRect, string guid, int instanceId)
{
var isFolder = AssetDatabase.IsValidFolder(guid.ToPath());
void setLastVisibleSelectedForAltClick()
{
if (!isFolder) return;
if (!curEvent.isRepaint) return;
if (!Selection.objects.Contains(AssetDatabase.LoadAssetAtPath<DefaultAsset>(guid.ToPath()))) return;
lastVisibleSelectedCellRect = cellRect;
}
void drawing()
{
if (!curEvent.isRepaint) { namesDrawnForGuids.Clear(); return; }
var folderInfo = isFolder ? GetFolderInfo(guid) : null;
var showingSecondNameLine = false;
void hideIcon()
{
if (!isFolder) return;
if (!folderInfo.hasColor) return;
cellRect.SetHeight(cellRect.width).Resize(4).Draw(EditorGUIUtility.isProSkin ? Greyscale(.2f) : Greyscale(.75f));
}
void icon()
{
if (!isFolder) return;
if (!folderInfo.hasColor && !folderInfo.hasIcon) return;
DrawBigFolderIcon(cellRect, folderInfo);
}
void twoLineName()
{
if (namesDrawnForGuids.Contains(guid)) return; // disables two-line names on subassets
if (!VFoldersMenu.twoLineNamesEnabled) return;
var isSelected = listArea_dragSelectionList.Any() ? listArea_dragSelectionList.Contains(instanceId) : Selection.instanceIDs.Contains(instanceId);
var isCellBeingRenamed = isSelected && renamingCell;
if (isCellBeingRenamed) return;
var maxLineWidth = cellRect.width + 14;
var name = guid.ToPath().GetFilename(withExtension: false); // Resources.InstanceIDToObject(instanceId).name;
string firstLine = null;
string secondLine = null;
Rect firstLineRect = default;
Rect secondLineRect = default;
void getCachedLines()
{
if (twoLineNamesCachedForCellWidth != cellRect.width)
twoLineNamesCache.Clear();
if (!twoLineNamesCache.ContainsKey(name)) return;
firstLine = twoLineNamesCache[name].Item1;
secondLine = twoLineNamesCache[name].Item2;
}
void calcLines()
{
if (twoLineNamesCache.ContainsKey(name)) return;
if (name.GetLabelWidth() < maxLineWidth)
{
firstLine = name;
twoLineNamesCache[name] = (firstLine, secondLine);
twoLineNamesCachedForCellWidth = cellRect.width;
return;
}
var separators = new[] { ' ', '_', '-', '.' };
var splitIndexes_separators = Enumerable.Range(1, name.Length - 2).Where(i => separators.Contains(name[i])).Select(i => i + 1);
var splitIndexes_camelcase = Enumerable.Range(2, name.Length - 2).Where(i => name[i].IsUpper() && name[i - 1].IsLower() && !separators.Contains(name[i - 1]));
var splitIndexes_all = Enumerable.Range(1, name.Length - 1);
bool splitSucceeded = false;
string fit(string s, IEnumerable<int> splitIndexes, bool splitToRight, bool addEllipsis = false)
{
foreach (var i in splitToRight ? splitIndexes : splitIndexes.Reverse())
{
var substring = splitToRight ? s[i..] : s[..i];
substring = substring.Trim();
if (addEllipsis)
substring += "…";
if (substring.GetLabelWidth() < maxLineWidth)
return substring;
}
return null;
}
void truncate()
{
if (splitIndexes_separators.Any()) return;
if (splitIndexes_camelcase.Any()) return;
firstLine = fit(name, splitIndexes_all, splitToRight: false, addEllipsis: true);
splitSucceeded = true;
}
void split_clean()
{
if (splitSucceeded) return;
firstLine = null;
firstLine ??= fit(name, splitIndexes_separators, splitToRight: false);
firstLine ??= fit(name, splitIndexes_camelcase, splitToRight: false);
if (firstLine == null) return;
secondLine = name.Remove(firstLine);
splitSucceeded = secondLine.GetLabelWidth() < maxLineWidth;
}
void split_withEllipsis()
{
if (splitSucceeded) return;
secondLine = null;
secondLine ??= fit(name, splitIndexes_separators, splitToRight: true);
secondLine ??= fit(name, splitIndexes_camelcase, splitToRight: true);
secondLine ??= fit(name, splitIndexes_all, splitToRight: true);
firstLine = name.Remove(secondLine).Trim();
if (firstLine.GetLabelWidth() > maxLineWidth)
firstLine = fit(firstLine, Enumerable.Range(0, firstLine.Length), splitToRight: false, addEllipsis: true);
}
truncate();
split_clean();
split_withEllipsis();
firstLine = firstLine.Trim();
secondLine = secondLine?.Trim();
twoLineNamesCache[name] = (firstLine, secondLine);
twoLineNamesCachedForCellWidth = cellRect.width;
}
void calcLineRects()
{
firstLineRect = cellRect.SetHeightFromBottom(12).AddHeight(2).SetWidthFromMid(maxLineWidth).MoveY(-1);
if (secondLine.IsNullOrEmpty()) return;
if (isFolder)
firstLineRect = firstLineRect.MoveY(-4);
else
firstLineRect = firstLineRect.MoveY(-1);
secondLineRect = firstLineRect.MoveY(12);
}
void hideDefaultName()
{
if (!curEvent.isRepaint) return;
var defaultNameRect = cellRect.SetHeightFromBottom(16).AddHeight(2).AddWidthFromMid(12);
var maskColor = isDarkTheme ? Greyscale(.2f) : Greyscale(.75f);
defaultNameRect.Draw(maskColor);
}
void selectedbackground()
{
if (!isSelected) return;
var backgroundColor = isListAreaFocused ? GUIColors.selectedBackground : Greyscale(isDarkTheme ? .3f : .68f);
var longestLine = secondLine?.Length > firstLine.Length ? secondLine : firstLine;
var backgroundRect = firstLineRect.SetWidthFromMid(longestLine.GetLabelWidth(fontSize: 10) + 2).SetHeightFromMid(14);
if (!secondLine.IsNullOrEmpty())
backgroundRect = backgroundRect.SetYMax(secondLineRect.SetHeightFromMid(14).yMax);
backgroundRect.DrawRounded(backgroundColor, 4);
}
void drawName()
{
SetLabelAlignmentCenter();
SetLabelFontSize(10);
SetGUIColor(isSelected && isListAreaFocused ? Greyscale(123, 123) : Greyscale(1));
GUI.Label(firstLineRect, firstLine);
if (secondLine != null)
GUI.Label(secondLineRect, secondLine);
ResetGUIColor();
ResetLabelStyle();
}
getCachedLines();
calcLines();
calcLineRects();
hideDefaultName();
selectedbackground();
drawName();
showingSecondNameLine = secondLine != null;
namesDrawnForGuids.Add(guid);
}
void hoverHighlight()
{
var highlightColor = isDarkTheme ? Greyscale(1, .058f) : Greyscale(0, .07f);
var highlightRect = cellRect.Resize(-2).AddHeight(4).AddWidthFromMid(4);
if (showingSecondNameLine)
highlightRect = highlightRect.AddHeight(8);
var hoverRect = cellRect.AddWidthFromMid(gridHorizontalSpacing).AddHeight(gridVerticalSpacing);
hoverRect.MarkInteractive();
if (!VFoldersPaletteWindow.instance)
if (!hoverRect.IsHovered()) return;
if (VFoldersPaletteWindow.instance)
if (!VFoldersPaletteWindow.instance.guids.Contains(guid) || !paletteOpenedOnCell) return;
highlightRect.DrawRounded(highlightColor, 5);
}
hideIcon();
icon();
twoLineName();
hoverHighlight();
}
void altDrag()
{
if (!isFolder) return;
if (!curEvent.holdingAlt) return;
void mouseDown()
{
if (!curEvent.isMouseDown) return;
if (!cellRect.IsHovered()) return;
mouseDownPos = curEvent.mousePosition;
}
void mouseDrag()
{
if (!curEvent.isMouseDrag) return;
if ((curEvent.mousePosition - mouseDownPos).magnitude < 5) return;
if (!cellRect.Contains(mouseDownPos)) return;
if (!cellRect.Contains(curEvent.mousePosition - curEvent.mouseDelta)) return;
if (DragAndDrop.objectReferences.Any()) return;
DragAndDrop.PrepareStartDrag();
DragAndDrop.objectReferences = new[] { AssetDatabase.LoadAssetAtPath<Object>(guid.ToPath()) };
DragAndDrop.StartDrag(guid.ToPath().GetFilename());
}
mouseDown();
mouseDrag();
}
void altClick()
{
if (!isFolder) return;
if (!cellRect.IsHovered()) return;
if (!curEvent.holdingAlt) return;
void mouseDown()
{
if (!curEvent.isMouseDown) return;
curEvent.Use();
}
void mouseUp()
{
if (!curEvent.isMouseUp) return;
var selectedFoldersGuids = Selection.objects.Where(r => r is DefaultAsset).Select(r => r.GetPath().ToGuid());
var editMultiSelection = selectedFoldersGuids.Count() > 1 && selectedFoldersGuids.Contains(guid);
var guidsToEdit = (editMultiSelection ? selectedFoldersGuids : new[] { guid }).ToList();
if (VFoldersPaletteWindow.instance && VFoldersPaletteWindow.instance.guids.SequenceEqual(guidsToEdit)) { VFoldersPaletteWindow.instance.Close(); return; }
var openNearRect = editMultiSelection ? lastVisibleSelectedCellRect : cellRect;
var position = EditorGUIUtility.GUIToScreenPoint(new Vector2(openNearRect.xMax + 10, openNearRect.y - 5));
if (!VFoldersPaletteWindow.instance)
VFoldersPaletteWindow.CreateInstance(position);
VFoldersPaletteWindow.instance.Init(guidsToEdit);
VFoldersPaletteWindow.instance.Focus();
VFoldersPaletteWindow.instance.targetPosition = position;
paletteOpenedOnCell = true;
paletteOpenedOnRow = false;
if (editMultiSelection)
Selection.objects = null;
}
mouseDown();
mouseUp();
}
setLastVisibleSelectedForAltClick();
drawing();
altDrag();
altClick();
}
Rect lastVisibleSelectedCellRect;
HashSet<string> namesDrawnForGuids = new();
Dictionary<string, (string, string)> twoLineNamesCache = new();
float twoLineNamesCachedForCellWidth;
bool paletteOpenedOnCell;
public void UpdateState_Layout()
{
isOneColumn = window.GetFieldValue<int>("m_ViewMode") == 0;
listArea = isTwoColumns ? window.GetFieldValue("m_ListArea") : null;
treeViewController = window.GetFieldValue(isTwoColumns ? "m_FolderTree" : "m_AssetTree");
treeViewControllerData = treeViewController?.GetPropertyValue("data");
rows = treeViewControllerData?.InvokeMethod<IList<TreeViewItem>>("GetRows");
2025-08-18 09:22:24 +08:00
var treeViewState = treeViewController?.GetPropertyValue<TreeViewState>("state");
2025-08-18 09:22:24 +08:00
expandedIds = treeViewState?.expandedIDs ?? new List<int>();
var treeViewAnimator = treeViewController?.GetMemberValue("m_ExpansionAnimator");
treeViewAnimatesExpansion = treeViewAnimator?.GetMemberValue<bool>("isAnimating") ?? false;
}
public void UpdateState_Repaint()
{
var treeViewControlID = window.GetFieldValue<int>("m_TreeViewKeyboardControlID");
var listAreaControlID = window.GetFieldValue<int>("m_ListKeyboardControlID");
isTreeFocused = EditorWindow.focusedWindow == window && (GUIUtility.keyboardControl == treeViewControlID || treeViewControlID == 0 || treeViewControlID == -1);
isListAreaFocused = EditorWindow.focusedWindow == window && (GUIUtility.keyboardControl == listAreaControlID || listAreaControlID == 0 || listAreaControlID == -1);
renamingRow = EditorGUIUtility.editingTextField && treeViewController?.GetMemberValue("state")?.GetMemberValue("renameOverlay")?.InvokeMethod<bool>("IsRenaming") == true;
renamingCell = EditorGUIUtility.editingTextField && listArea?.InvokeMethod("GetRenameOverlay")?.InvokeMethod<bool>("IsRenaming") == true;
var grid = listArea?.GetMemberValue("m_LocalAssets")?.GetMemberValue("m_Grid");
gridHorizontalSpacing = grid?.GetMemberValue<float>("horizontalSpacing") ?? 0;
gridVerticalSpacing = grid?.GetMemberValue<float>("verticalSpacing") ?? 0;
listArea_dragSelectionList = listArea?.GetMemberValue("m_LocalAssets")?.GetMemberValue<List<int>>("m_DragSelection") ?? new();
treeView_dragSelectionList = treeViewController?.GetFieldValue("m_DragSelection")?.GetFieldValue<List<int>>("m_List") ?? new();
treeView_normalSelectionList = isTwoColumns ? treeViewController?.GetFieldValue("m_CachedSelection")?.GetFieldValue<List<int>>("m_List") ?? new() : null;
// only treeView_normalSelectionList must be updated in repaint, the rest can be moved to UpdateState_Layout
// but they are all grouped here since they are only used for drawing
}
bool isOneColumn;
bool isTwoColumns => !isOneColumn;
bool treeViewAnimatesExpansion;
public object listArea;
public object treeViewController;
public object treeViewControllerData;
public IList<TreeViewItem> rows;
2025-08-18 09:22:24 +08:00
public List<int> expandedIds = new();
bool isTreeFocused;
bool isListAreaFocused;
bool renamingRow;
bool renamingCell;
float gridVerticalSpacing;
float gridHorizontalSpacing;
List<int> listArea_dragSelectionList = new();
List<int> treeView_dragSelectionList = new();
List<int> treeView_normalSelectionList = new();
public void UpdateFoldersFirst()
{
if (!VFoldersMenu.foldersFirstEnabled) return;
if (Application.platform != RuntimePlatform.OSXEditor) return;
void oneColumn()
{
if (isTwoColumns) return;
if (foldersFirst_initedForOneColumn) return;
var m_AssetTree = window.GetFieldValue("m_AssetTree");
if (m_AssetTree == null) return;
m_AssetTree.GetPropertyValue("data").SetPropertyValue("foldersFirst", true);
m_AssetTree.InvokeMethod("ReloadData");
foldersFirst_initedForOneColumn = true;
foldersFirst_initedForTwoColumns = false;
}
void twoColumns()
{
if (!isTwoColumns) return;
if (foldersFirst_initedForTwoColumns) return;
var m_ListArea = window.GetFieldValue("m_ListArea");
if (m_ListArea == null) return;
m_ListArea.SetPropertyValue("foldersFirst", true);
window.InvokeMethod("InitListArea");
foldersFirst_initedForOneColumn = false;
foldersFirst_initedForTwoColumns = true;
}
oneColumn();
twoColumns();
}
bool foldersFirst_initedForOneColumn;
bool foldersFirst_initedForTwoColumns;
public VFoldersGUI(EditorWindow window) => this.window = window;
public EditorWindow window;
public VFoldersController controller => VFolders.controllers_byWindow[window];
}
}
#endif