#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; 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("FindItem", instanceId); } 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; 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(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>("m_List") #else treeViewController?.GetMemberValue("state").GetMemberValue>("selectedIDs") #endif .Select(id => treeViewController.InvokeMethod("FindItem", id)) .Where(r => r?.GetType().Name == "FolderTreeItem") .Select(r => r.GetPropertyValue("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 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(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 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(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 namesDrawnForGuids = new(); Dictionary twoLineNamesCache = new(); float twoLineNamesCachedForCellWidth; bool paletteOpenedOnCell; public void UpdateState_Layout() { isOneColumn = window.GetFieldValue("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>("GetRows"); var treeViewState = treeViewController?.GetPropertyValue("state"); expandedIds = treeViewState?.expandedIDs ?? new List(); var treeViewAnimator = treeViewController?.GetMemberValue("m_ExpansionAnimator"); treeViewAnimatesExpansion = treeViewAnimator?.GetMemberValue("isAnimating") ?? false; } public void UpdateState_Repaint() { var treeViewControlID = window.GetFieldValue("m_TreeViewKeyboardControlID"); var listAreaControlID = window.GetFieldValue("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("IsRenaming") == true; renamingCell = EditorGUIUtility.editingTextField && listArea?.InvokeMethod("GetRenameOverlay")?.InvokeMethod("IsRenaming") == true; var grid = listArea?.GetMemberValue("m_LocalAssets")?.GetMemberValue("m_Grid"); gridHorizontalSpacing = grid?.GetMemberValue("horizontalSpacing") ?? 0; gridVerticalSpacing = grid?.GetMemberValue("verticalSpacing") ?? 0; listArea_dragSelectionList = listArea?.GetMemberValue("m_LocalAssets")?.GetMemberValue>("m_DragSelection") ?? new(); treeView_dragSelectionList = treeViewController?.GetFieldValue("m_DragSelection")?.GetFieldValue>("m_List") ?? new(); treeView_normalSelectionList = isTwoColumns ? treeViewController?.GetFieldValue("m_CachedSelection")?.GetFieldValue>("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 rows; public List expandedIds = new(); bool isTreeFocused; bool isListAreaFocused; bool renamingRow; bool renamingCell; float gridVerticalSpacing; float gridHorizontalSpacing; List listArea_dragSelectionList = new(); List treeView_dragSelectionList = new(); List 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