using UnityEditor; using UnityEngine; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace P4Connect { public class Icons { const float MinHeight = 16.0f; const float MinWidth = 32.0f; const float MaxWidth = 96.0f; const float IconMinSize = 8.0f; const float IconMaxSize = 24.0f; const float MarginX = 1.0f; const float MarginY = 0.0f; enum Corner { BottomRight = 0, BottomLeft, TopLeft, TopRight, Center } static Texture IconHasLatest; static Texture IconOutOfDate; static Texture IconCheckedOutLocal; static Texture IconCheckedOutOther; static Texture IconMarkedForAddLocal; static Texture IconMarkedForAddOther; static Texture IconMarkedForDeleteLocal; static Texture IconMarkedForDeleteOther; static Texture IconMoveAdd; static Texture IconMoveDelete; static Texture IconMissingFile; static Texture IconOtherFile; static Texture IconPairFileOnly; static Texture IconPairMetaOnly; static Texture IconPairFileAndMeta; static Texture IconNeedsResolve; static Texture IconFolderMetaOutOfDate; static Texture IconFolderMetaModifiedLocal; static Texture IconLockedLocal; static Texture IconLockedOther; enum UpdateState { None, ProjectWindowChanged, TriggerProjectWindowChanged, } static UpdateState _State; static string _CurrentFolder; static HashSet _FilesToUpdate; public static void Initialize() { string iconsPack = Utils.FullPathToAssetPath(System.IO.Path.Combine(Utils.GetDLLFullDirectory(), "icons.pack")); AssetBundle icons = AssetBundle.CreateFromFile(iconsPack); if (icons != null) { IconHasLatest = icons.Load("Has-Latest", typeof(Texture)) as Texture; IconOutOfDate = icons.Load("Out-of-date", typeof(Texture)) as Texture; IconCheckedOutLocal = icons.Load("Checked-out-Local", typeof(Texture)) as Texture; IconCheckedOutOther = icons.Load("Checked-out-Other", typeof(Texture)) as Texture; IconMarkedForAddLocal = icons.Load("Marked-for-add-Local", typeof(Texture)) as Texture; IconMarkedForAddOther = icons.Load("Marked-for-add-Other", typeof(Texture)) as Texture; IconMarkedForDeleteLocal = icons.Load("Marked-for-delete-Local", typeof(Texture)) as Texture; IconMarkedForDeleteOther = icons.Load("Marked-for-delete-Other", typeof(Texture)) as Texture; IconMoveAdd = icons.Load("Move-Add", typeof(Texture)) as Texture; IconMoveDelete = icons.Load("Move-Delete", typeof(Texture)) as Texture; IconMissingFile = icons.Load("missing-file", typeof(Texture)) as Texture; IconOtherFile = icons.Load("other-file", typeof(Texture)) as Texture; IconPairFileOnly = icons.Load("has-file", typeof(Texture)) as Texture; IconPairMetaOnly = icons.Load("has-meta", typeof(Texture)) as Texture; IconPairFileAndMeta = icons.Load("has-file-and-meta", typeof(Texture)) as Texture; IconNeedsResolve = icons.Load("needs-resolve", typeof(Texture)) as Texture; IconFolderMetaOutOfDate = icons.Load("foldermeta-out-of-date", typeof(Texture)) as Texture; IconFolderMetaModifiedLocal = icons.Load("foldermeta-modified-local", typeof(Texture)) as Texture; IconLockedLocal = icons.Load("locked-local", typeof(Texture)) as Texture; IconLockedOther = icons.Load("locked-other", typeof(Texture)) as Texture; icons.Unload(false); _State = UpdateState.None; _FilesToUpdate = new HashSet(); UpdateDisplay(); } } public static void UpdateDisplay() { EditorApplication.projectWindowItemOnGUI -= OnProjectWindowItem; EditorApplication.update -= OnUpdate; AssetStatusCache.OnAssetStatusChanged -= OnOperationPerformed; if (Config.PerforceEnabled && Config.DisplayStatusIcons) { EditorApplication.projectWindowItemOnGUI += OnProjectWindowItem; EditorApplication.update += OnUpdate; AssetStatusCache.OnAssetStatusChanged += OnOperationPerformed; } _State = UpdateState.TriggerProjectWindowChanged; EditorApplication.RepaintProjectWindow(); } static void OnProjectWindowItem(String aGUID, Rect aSelectionRect) { // What kind of asset is this? string assetPath = AssetDatabase.GUIDToAssetPath(aGUID); DrawItemIcons(assetPath, aSelectionRect, aSelectionRect.height <= 16.0f); if (_State == UpdateState.ProjectWindowChanged) { _FilesToUpdate.Add(assetPath); } } static void OnUpdate() { // Don't update icons while in play mode if (Config.PerforceEnabled && Config.DisplayStatusIcons && !EditorApplication.isPlayingOrWillChangePlaymode) { if (_State == UpdateState.TriggerProjectWindowChanged) { _FilesToUpdate.Clear(); _State = UpdateState.ProjectWindowChanged; // Force a refresh AssetDatabase.Refresh(); } else if (_State == UpdateState.ProjectWindowChanged) { // Now we update the state of all the flags List validFiles = new List(); foreach (var file in _FilesToUpdate) { if (file != "" && file != "Assets") { validFiles.Add(file); } } // Merge in the list of assets from the current folder var sel = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets); if (sel != null && sel.Length > 0) { string assetPath = AssetDatabase.GetAssetPath(sel[0]); string assetFullPath = Utils.AssetPathToFullPath(assetPath); string folderFullPath; if (Utils.IsDirectory(assetPath)) folderFullPath = assetFullPath; else folderFullPath = System.IO.Path.GetDirectoryName(assetFullPath); foreach (var name in System.IO.Directory.GetFileSystemEntries(folderFullPath)) { string subAssetPath = Utils.FullPathToAssetPath(name); if (!validFiles.Contains(subAssetPath)) validFiles.Add(subAssetPath); } } AssetStatusCache.UpdateAssetData(validFiles); _FilesToUpdate.Clear(); _State = UpdateState.None; AssetDatabase.Refresh(); } else if (_State == UpdateState.None) { // Detect change in selection var selection = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets); if (selection != null && selection.Length > 0) { string selectionFolder = AssetDatabase.GetAssetPath(selection[0]); if (!Utils.IsDirectory(selectionFolder)) { selectionFolder = System.IO.Path.GetDirectoryName(selectionFolder); } if (_CurrentFolder != selectionFolder) { _CurrentFolder = selectionFolder; _FilesToUpdate.Clear(); _State = UpdateState.ProjectWindowChanged; } } } } } static void OnOperationPerformed(PerforceConnection aConnection) { // Refresh the project view _State = UpdateState.TriggerProjectWindowChanged; } public static void DrawItemIcons(String aAssetPath, Rect aSelectionRect, bool aHorizontal) { CheckInit(); // What kind of asset is this? if (aAssetPath != "" && aAssetPath != "Assets") { bool isFolder = Utils.IsDirectory(aAssetPath); AssetStatusCache.AssetStatus status; if (isFolder) status = AssetStatusCache.GetAssetData(Utils.MetaFromAsset(aAssetPath)); else status = AssetStatusCache.GetAssetData(aAssetPath); DrawItemIcons(status, isFolder, aSelectionRect, aHorizontal); } } public static void DrawItemIcons(AssetStatusCache.AssetStatus aStatus, bool aIsFolder, Rect aSelectionRect, bool aHorizontal) { CheckInit(); Rect iconRect = GetIconRect(aSelectionRect, Corner.TopLeft, aHorizontal); switch (aStatus.LocalState) { case FileState.None: break; case FileState.InDepot: break; case FileState.MarkedForEdit: if (aIsFolder) GUI.DrawTexture(iconRect, IconFolderMetaModifiedLocal); else GUI.DrawTexture(iconRect, IconCheckedOutLocal); break; case FileState.MarkedForAdd: if (aIsFolder) GUI.DrawTexture(iconRect, IconFolderMetaModifiedLocal); else GUI.DrawTexture(iconRect, IconMarkedForAddLocal); break; case FileState.MarkedForDelete: if (aIsFolder) GUI.DrawTexture(iconRect, IconFolderMetaModifiedLocal); else GUI.DrawTexture(iconRect, IconMarkedForDeleteLocal); break; case FileState.MarkedForAddMove: if (aIsFolder) GUI.DrawTexture(iconRect, IconFolderMetaModifiedLocal); else GUI.DrawTexture(iconRect, IconMoveAdd); break; case FileState.MarkedForDeleteMove: if (aIsFolder) GUI.DrawTexture(iconRect, IconFolderMetaModifiedLocal); else GUI.DrawTexture(iconRect, IconMoveDelete); break; } iconRect = GetIconRect(aSelectionRect, Corner.TopRight, aHorizontal); switch (aStatus.OtherState) { case FileState.None: break; case FileState.InDepot: break; case FileState.MarkedForEdit: if (!aIsFolder) { GUI.DrawTexture(iconRect, IconCheckedOutOther); } break; case FileState.MarkedForAdd: if (!aIsFolder) { GUI.DrawTexture(iconRect, IconMarkedForAddOther); } break; case FileState.MarkedForDelete: if (!aIsFolder) { GUI.DrawTexture(iconRect, IconMarkedForDeleteOther); } break; case FileState.MarkedForAddMove: if (!aIsFolder) { GUI.DrawTexture(iconRect, IconMarkedForAddOther); } break; case FileState.MarkedForDeleteMove: if (!aIsFolder) { GUI.DrawTexture(iconRect, IconMarkedForDeleteOther); } break; } iconRect = GetIconRect(aSelectionRect, Corner.BottomRight, aHorizontal); switch (aStatus.LocalState) { case FileState.None: break; case FileState.InDepot: switch (aStatus.RevisionState) { case RevisionState.None: break; case RevisionState.HasLatest: if (!aIsFolder) { GUI.DrawTexture(iconRect, IconHasLatest); } break; case RevisionState.OutOfDate: if (aIsFolder) GUI.DrawTexture(iconRect, IconFolderMetaOutOfDate); else GUI.DrawTexture(iconRect, IconOutOfDate); break; } break; case FileState.MarkedForEdit: goto case FileState.InDepot; case FileState.MarkedForAdd: break; case FileState.MarkedForDelete: goto case FileState.InDepot; case FileState.MarkedForAddMove: if (!aIsFolder) { GUI.DrawTexture(iconRect, IconHasLatest); } break; case FileState.MarkedForDeleteMove: goto case FileState.InDepot; } switch (aStatus.ResolvedState) { case ResolvedState.None: break; case ResolvedState.NeedsResolve: if (aIsFolder) { iconRect = GetIconRect(aSelectionRect, Corner.BottomRight, aHorizontal); GUI.DrawTexture(iconRect, IconNeedsResolve); } else { iconRect = GetIconRect(aSelectionRect, Corner.Center, aHorizontal); GUI.DrawTexture(iconRect, IconNeedsResolve); } break; } switch (aStatus.LockState) { case LockState.None: break; case LockState.OurLock: iconRect = GetIconRect(aSelectionRect, Corner.BottomLeft, aHorizontal); GUI.DrawTexture(iconRect, IconLockedLocal); break; case LockState.TheirLock: iconRect = GetIconRect(aSelectionRect, Corner.BottomLeft, aHorizontal); GUI.DrawTexture(iconRect, IconLockedOther); break; } } public static Texture GetAssetIcon(string arPath) { CheckInit(); Texture asset = AssetDatabase.GetCachedIcon(arPath); // If we still don't have an icon, use a default one if (asset == null) { if (arPath.StartsWith("Assets/")) { asset = IconMissingFile; } else { asset = IconOtherFile; } } return asset; } public static Texture GetFileAndMetaTypeIcon(FileAndMetaType aType) { CheckInit(); Texture ret = null; switch (aType) { case FileAndMetaType.FileOnly: ret = IconPairFileOnly; break; case FileAndMetaType.MetaOnly: ret = IconPairMetaOnly; break; case FileAndMetaType.FileAndMeta: ret = IconPairFileAndMeta; break; } return ret; } static Rect GetIconRect(Rect aSelectionRect, Corner aCorner, bool aHorizontal) { Rect iconRect = aSelectionRect; float iconSize = 0.0f; if (aHorizontal) { // The view is in list mode iconSize = IconMinSize; switch (aCorner) { case Corner.BottomRight: iconRect.xMin += MinHeight - iconSize + MarginX; iconRect.yMin = iconRect.yMax - iconSize + MarginY; break; case Corner.BottomLeft: iconRect.xMin -= MarginX; iconRect.yMin = iconRect.yMax - iconSize + MarginY; break; case Corner.TopLeft: iconRect.xMin -= MarginX; iconRect.yMin -= MarginY; break; case Corner.TopRight: iconRect.xMin += MinHeight - iconSize + MarginX; iconRect.yMin -= MarginY; break; case Corner.Center: iconRect.xMin += 1.0f; iconRect.xMax = iconRect.xMin + MinHeight - 2.0f; iconRect.yMin += 1.0f; iconRect.yMax -= 1.0f; break; } } else { // The view is in grid mode float sizePercent = (aSelectionRect.width - MinWidth) / (MaxWidth - MinWidth); iconSize = Mathf.Round(Mathf.Lerp(IconMinSize, IconMaxSize, sizePercent)); switch (aCorner) { case Corner.BottomRight: iconRect.xMin = iconRect.xMax - iconSize + MarginX; iconRect.yMin = iconRect.yMax - MinHeight - iconSize + MarginY; break; case Corner.BottomLeft: iconRect.xMin -= MarginX; iconRect.yMin = iconRect.yMax - MinHeight - iconSize + MarginY; break; case Corner.TopLeft: iconRect.xMin -= MarginX; iconRect.yMin -= MarginY; break; case Corner.TopRight: iconRect.xMin = iconRect.xMax - iconSize + MarginX; iconRect.yMin -= MarginY; break; case Corner.Center: iconRect.yMax -= MinHeight + 1.0f; iconRect.yMin += 2.0f; iconRect.xMin += 2.0f; iconRect.xMax -= 2.0f; break; } } if (aCorner != Corner.Center) { iconRect.width = iconSize; iconRect.height = iconSize; } return iconRect; } static void CheckInit() { if (IconHasLatest == null) { Initialize(); } } } }