#if UNITY_EDITOR using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using UnityEditor.ShortcutManagement; using System.Reflection; using System.Linq; using UnityEngine.UIElements; using UnityEngine.SceneManagement; using UnityEditor.SceneManagement; using Type = System.Type; using static VFolders.Libs.VUtils; using static VFolders.Libs.VGUI; using static VFolders.VFolders; using static VFolders.VFoldersData; using static VFolders.VFoldersPalette; using static VFolders.VFoldersCache; namespace VFolders { public class VFoldersPaletteWindow : EditorWindow { void OnGUI() { if (!palette) { Close(); return; } int hoveredColorIndex = -1; string hoveredIconNameOrGuid = null; void background() { position.SetPos(0, 0).Draw(windowBackground); } void outline() { if (Application.platform == RuntimePlatform.OSXEditor) return; position.SetPos(0, 0).DrawOutline(Greyscale(.1f)); } void colors() { if (!palette.colorsEnabled) { Space(-spaceAfterColors); return; } var rowRect = ExpandWidthLabelRect(height: cellSize).SetX(paddingX); void color(int i) { var cellRect = rowRect.MoveX(i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); void backgroundSelected() { if (!initialColorIndexes.Contains(i)) return; cellRect.Resize(1).DrawWithRoundedCorners(selectedBackground, 2); } void backgroundHovered() { if (!cellRect.IsHovered()) return; cellRect.Resize(1).DrawWithRoundedCorners(this.hoveredBackground, 2); } void crossIcon() { if (i != 0) return; SetLabelAlignmentCenter(); GUI.Label(cellRect.SetSizeFromMid(iconSize), EditorGUIUtility.IconContent("CrossIcon")); ResetLabelStyle(); } void color() { if (i == 0) return; var brightness = 1.00f; var outlineColor = Greyscale(.15f, .2f); cellRect.Resize(3).DrawWithRoundedCorners(outlineColor, 4); cellRect.Resize(4).DrawWithRoundedCorners((palette.colors[i - 1] * brightness).SetAlpha(1), 3); cellRect.Resize(4).AddWidthFromRight(-2).DrawCurtainLeft(GUIColors.windowBackground.SetAlpha((1 - palette.colors[i - 1].a) * .45f)); // cellRect.Resize(4).AddWidthFromRight(-2).DrawCurtainLeft(GUIColors.windowBackground.SetAlpha((1 - .1f) * .45f)); } void setHovered() { if (!cellRect.IsHovered()) return; hoveredColorIndex = i; } void closeOnClick() { if (!cellRect.IsHovered()) return; if (!curEvent.isMouseDown) return; Close(); } cellRect.MarkInteractive(); backgroundSelected(); backgroundHovered(); crossIcon(); color(); setHovered(); closeOnClick(); } for (int i = 0; i < palette.colors.Count + 1; i++) color(i); } void icons() { void row(IconRow iconRow) { if (!iconRow.enabled) return; if (iconRow.isEmpty) return; var rowRect = ExpandWidthLabelRect(height: cellSize).SetX(paddingX); var isFirstEnabledRow = palette.iconRows.First(r => r.enabled) == iconRow; void icon(int i) { var cellRect = rowRect.MoveX(i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); var isCrossIcon = isFirstEnabledRow && i == 0; var actualIconIndex = isFirstEnabledRow ? i - 1 : i; var isBuiltinIcon = !isCrossIcon && actualIconIndex < iconRow.builtinIcons.Count; var isCustomIcon = !isCrossIcon && actualIconIndex >= iconRow.builtinIcons.Count; var iconNameOrGuid = isCrossIcon ? "" : isCustomIcon ? iconRow.customIcons[actualIconIndex - iconRow.builtinIcons.Count] : iconRow.builtinIcons[actualIconIndex]; void backgroundSelected() { if (!initialIconNamesOrGuids.Contains(iconNameOrGuid)) return; cellRect.Resize(1).DrawWithRoundedCorners(selectedBackground, 2); } void backgroundHovered() { if (!cellRect.IsHovered()) return; cellRect.Resize(1).DrawWithRoundedCorners(this.hoveredBackground, 2); } void crossIcon() { if (!isCrossIcon) return; SetLabelAlignmentCenter(); GUI.Label(cellRect.SetSizeFromMid(iconSize), EditorGUIUtility.IconContent("CrossIcon")); ResetLabelStyle(); } void builtinIcon() { if (!isBuiltinIcon) return; SetLabelAlignmentCenter(); GUI.Label(cellRect.SetSizeFromMid(iconSize), EditorGUIUtility.IconContent(iconNameOrGuid)); ResetLabelStyle(); } void customIcon() { if (!isCustomIcon) return; var texture = AssetDatabase.LoadAssetAtPath(iconNameOrGuid.ToPath()); GUI.DrawTexture(cellRect.SetSizeFromMid(iconSize), texture ?? Texture2D.blackTexture); } void setHovered() { if (!cellRect.IsHovered()) return; hoveredIconNameOrGuid = iconNameOrGuid; } void closeOnClick() { if (!cellRect.IsHovered()) return; if (!curEvent.isMouseDown) return; Close(); } cellRect.MarkInteractive(); backgroundSelected(); backgroundHovered(); crossIcon(); builtinIcon(); customIcon(); setHovered(); closeOnClick(); } for (int i = 0; i < iconRow.iconCount + (isFirstEnabledRow ? 1 : 0); i++) icon(i); Space(rowSpacing - 2); } for (int i = 0; i < palette.iconRows.Count; i++) row(palette.iconRows[i]); } void setColorsAndIcons() { if (!curEvent.isRepaint) return; if (palette.iconRows.Any(r => r.enabled)) if (hoveredIconNameOrGuid != null) SetIcon(hoveredIconNameOrGuid); else SetInitialIcons(); if (palette.colorsEnabled) if (hoveredColorIndex != -1) SetColor(hoveredColorIndex); else SetInitialColors(); } void updatePosition() { if (!curEvent.isLayout) return; void calcDeltaTime() { deltaTime = (float)(EditorApplication.timeSinceStartup - lastLayoutTime); if (deltaTime > .05f) deltaTime = .0166f; lastLayoutTime = EditorApplication.timeSinceStartup; } void resetCurPos() { if (currentPosition != default) return; currentPosition = position.position; // position.position is always int, which can't be used for lerping } void lerpCurPos() { var speed = 9; SmoothDamp(ref currentPosition, targetPosition, speed, ref positionDeriv, deltaTime); // Lerp(ref currentPosition, targetPosition, speed, deltaTime); } void setCurPos() { position = position.SetPos(currentPosition); } calcDeltaTime(); resetCurPos(); lerpCurPos(); setCurPos(); if (!currentPosition.magnitude.Approx(targetPosition.magnitude)) Repaint(); } void closeOnEscape() { if (!curEvent.isKeyDown) return; if (curEvent.keyCode != KeyCode.Escape) return; SetInitialColors(); SetInitialIcons(); Close(); } RecordUndoOnDatas(); background(); outline(); Space(paddingY); colors(); Space(spaceAfterColors); icons(); setColorsAndIcons(); updatePosition(); closeOnEscape(); EditorApplication.RepaintProjectWindow(); EditorApplication.delayCall += EditorApplication.RepaintProjectWindow; // to show icons that will be generated in update } static float iconSize => 18; static float iconSpacing => 2; static float cellSize => iconSize + iconSpacing; static float spaceAfterColors => 11; public float rowSpacing = 1; static float paddingX => 12; static float paddingY => 12; Color windowBackground => isDarkTheme ? Greyscale(.23f) : Greyscale(.7f); Color selectedBackground => isDarkTheme ? new Color(.3f, .5f, .7f, .8f) : new Color(.3f, .5f, .7f, .4f) * 1.35f; Color hoveredBackground = Greyscale(1, .3f); Color colorOutline => Greyscale(.2f, .5f); public Vector2 targetPosition; public Vector2 currentPosition; Vector2 positionDeriv; float deltaTime; double lastLayoutTime; void SetIcon(string iconNameOrGuid) { foreach (var r in folderInfos) r.iconNameOrGuid = iconNameOrGuid; } void SetColor(int colorIndex) { foreach (var r in folderInfos) r.folderData.colorIndex = colorIndex; } void SetInitialIcons() { for (int i = 0; i < folderInfos.Count; i++) folderInfos[i].iconNameOrGuid = initialIconNamesOrGuids[i]; } void SetInitialColors() { for (int i = 0; i < folderInfos.Count; i++) folderInfos[i].folderData.colorIndex = initialColorIndexes[i]; } void RemoveEmptyFolderDatas() { if (VFoldersData.storeDataInMetaFiles) return; // empties removed from meta files in SaveData() var toRemove = folderInfos.Select(r => r.folderData).Where(r => r.iconNameOrGuid == "" && r.colorIndex == 0); foreach (var r in toRemove) data.folderDatas_byGuid.RemoveValue(r); if (toRemove.Any()) Undo.CollapseUndoOperations(Undo.GetCurrentGroup() - 1); } void RecordUndoOnDatas() { if (!VFoldersData.storeDataInMetaFiles) if (data) data.RecordUndo(); if (VFoldersData.storeDataInMetaFiles) foreach (var r in guids) AssetImporter.GetAtPath(r.ToPath()).RecordUndo(); } void MarkDatasDirty() { if (!VFoldersData.storeDataInMetaFiles) if (data) data.Dirty(); if (VFoldersData.storeDataInMetaFiles) VFolders.folderDatasFromMetaFiles_byGuid.Clear(); } void SaveData() { if (!VFoldersData.storeDataInMetaFiles) { data.Save(); return; } for (int i = 0; i < guids.Count; i++) AssetImporter.GetAtPath(guids[i].ToPath()).userData = folderInfos[i].folderData.iconNameOrGuid == "" && folderInfos[i].folderData.colorIndex == 0 ? "" : JsonUtility.ToJson(folderInfos[i].folderData); for (int i = 0; i < guids.Count; i++) AssetImporter.GetAtPath(guids[i].ToPath()).SaveAndReimport(); } void OnLostFocus() { if (curEvent.holdingAlt && EditorWindow.focusedWindow?.GetType().Name == "ProjectBrowser") CloseNextFrameIfNotRefocused(); else Close(); } void CloseNextFrameIfNotRefocused() { EditorApplication.delayCall += () => { if (EditorWindow.focusedWindow != this) Close(); }; } public void Init(List guids) { void createData() { if (VFolders.data) return; VFolders.data = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(VFolders.data, GetScriptPath("VFolders").GetParentPath().CombinePath("vFolders Data.asset")); } void createPalette() { if (VFolders.palette) return; VFolders.palette = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(VFolders.palette, GetScriptPath("VFolders").GetParentPath().CombinePath("vFolders Palette.asset")); } void setSize() { var rowCellCounts = new List(); if (palette.colorsEnabled) rowCellCounts.Add(palette.colors.Count + 1); foreach (var r in palette.iconRows.Where(r => r.enabled)) rowCellCounts.Add(r.iconCount + (r == palette.iconRows.First(r => r.enabled) ? 1 : 0)); var width = rowCellCounts.Max() * cellSize + paddingX * 2; var iconRowCount = palette.iconRows.Count(r => r.enabled && !r.isEmpty); var rowCount = iconRowCount + (palette.colorsEnabled ? 1 : 0); var height = rowCount * (cellSize + rowSpacing) + (palette.colorsEnabled && palette.iconRows.Any(r => r.enabled && !r.isEmpty) ? spaceAfterColors : 0) + paddingY * 2; position = position.SetSize(width, height).SetPos(targetPosition); } void getInfos() { folderInfos.Clear(); foreach (var r in guids) folderInfos.Add(VFolders.GetFolderInfo(r, createDataIfDoesntExist: true)); } void getInitColorsAndIcons() { initialColorIndexes.Clear(); initialIconNamesOrGuids.Clear(); foreach (var r in folderInfos) initialColorIndexes.Add(r.folderData.colorIndex); foreach (var r in folderInfos) initialIconNamesOrGuids.Add(r.iconNameOrGuid); } this.guids = guids; RecordUndoOnDatas(); createData(); createPalette(); setSize(); getInfos(); getInitColorsAndIcons(); Undo.undoRedoPerformed -= EditorApplication.RepaintProjectWindow; Undo.undoRedoPerformed += EditorApplication.RepaintProjectWindow; } void OnDestroy() { RemoveEmptyFolderDatas(); MarkDatasDirty(); SaveData(); } public List guids = new List(); public List folderInfos = new List(); public List initialColorIndexes = new List(); public List initialIconNamesOrGuids = new List(); static VFoldersPalette palette => VFolders.palette; static VFoldersData data => VFolders.data; public static void CreateInstance(Vector2 position) { instance = ScriptableObject.CreateInstance(); instance.ShowPopup(); instance.position = instance.position.SetPos(position).SetSize(200, 300); instance.targetPosition = position; } public static VFoldersPaletteWindow instance; } } #endif