注:个人只在Unity2018及以上版本测试过,不保证其他版本是否能正常运行~
直接上测试效果图
使用方式
- 在Project窗口中选择任意文件夹
- 在Inspector窗口中编辑Comment
- 当关闭或者切换Inspector窗口时候会自动保存数据到文件中
下面就是源码了,喜欢的朋友可以直接拿去用
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
//------------------------------------------------------------
// shaco Framework
// Copyright © 2017-2020 chang.liu All rights reserved.
// Feedback: 449612236@qq.com
//------------------------------------------------------------
namespace shaco.Instance.ProjectFolderAsset
{
[CanEditMultipleObjects, CustomEditor(typeof(DefaultAsset))]
public class ProjectFolderAssetInspector : UnityEditor.Editor
{
private List<string> _assetsPath = new List<string>();
private string _comment = string.Empty;
private bool _isCommentChanged = false;
void OnEnable()
{
_assetsPath.Clear();
for (int i = 0; i < targets.Length; ++i)
{
var assetPathTmp = AssetDatabase.GetAssetPath(targets[i]);
if (!AssetDatabase.IsValidFolder(assetPathTmp))
continue;//@@@要支持多个设置
_assetsPath.Add(assetPathTmp);
}
if (_assetsPath.Count > 0)
_comment = ProjectFolderAssetDataManager.instance.AssetPathToComment(_assetsPath[0]);
}
void OnDestroy()
{
if (_isCommentChanged)
{
ProjectFolderAssetDataManager.instance.SaveToFile();
}
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
//只处理文件夹显示
if (0 == _assetsPath.Count)
return;
bool enabled = GUI.enabled;
GUI.enabled = true;
DrawFolder();
GUI.enabled = enabled;
}
private void DrawFolder()
{
EditorGUILayout.PrefixLabel("Comment");
GUI.changed = false;
_comment = EditorGUILayout.TextArea(_comment, GUILayout.MinHeight(48));
if (GUI.changed)
{
for (int i = 0; i < _assetsPath.Count; ++i)
ProjectFolderAssetDataManager.instance.SetComment(_assetsPath[i], _comment);
_isCommentChanged = true;
EditorUtility.SetDirty(target);
EditorApplication.RepaintProjectWindow();
}
}
}
/// <summary>
/// 编辑器文件夹绘制使用的相关数据管理器
/// </summary>
[InitializeOnLoad]
public class ProjectFolderAssetDataManager
{
public class ProjectFolderAssetData
{
//该路径仅仅作为参考(方便查看配置文本),并无实际意义
public string assetPath;
public string comment;
}
static public ProjectFolderAssetDataManager instance
{
get
{
if (null == _instance)
{
_instance = new ProjectFolderAssetDataManager();
_instance.LoadFromFile(_instance.SAVE_DATA_PATH);
}
return _instance;
}
}
static private ProjectFolderAssetDataManager _instance = null;
//保存路径
private string SAVE_DATA_PATH
{
get
{
return Application.dataPath + "/../ProjectSettings/shacoProjectFolderAssetData.txt";
}
}
//文件夹注释字典
private Dictionary<string, ProjectFolderAssetData> _folderDatas = new Dictionary<string, ProjectFolderAssetData>();
static ProjectFolderAssetDataManager()
{
EditorApplication.projectWindowItemOnGUI += ProjectWindowItemOnGUI;
}
/// <summary>
/// 获取文件夹注释
/// <param name="assetPath">文件夹路径</param>
/// <return>文件夹注释,如果没有则返回空字符串</return>
/// </summary>
public string AssetPathToComment(string assetPath)
{
var guid = AssetDatabase.AssetPathToGUID(assetPath);
return AssetGUIDToComment(guid);
}
/// <summary>
/// 获取文件夹注释
/// <param name="guid">文件夹唯一id</param>
/// <return>文件夹注释,如果没有则返回空字符串</return>
/// </summary>
public string AssetGUIDToComment(string guid)
{
string retValue = null;
ProjectFolderAssetData findData = null;
if (_folderDatas.TryGetValue(guid, out findData))
retValue = findData.comment;
else
retValue = string.Empty;
return retValue;
}
/// <summary>
/// 获取文件夹数据
/// <param name="guid">文件夹唯一id</param>
/// <return>文件夹数据,没有则返回空</return>
/// </summary>
public ProjectFolderAssetData GetFolderData(string guid)
{
ProjectFolderAssetData findData = null;
_folderDatas.TryGetValue(guid, out findData);
return findData;
}
/// <summary>
/// 设置文件夹注释,并自动保存到文件
/// <param name="assetPath">文件夹路径</param>
/// <param name="comment">文件夹注释</param>
/// </summary>
public void SetComment(string assetPath, string comment)
{
if (string.IsNullOrEmpty(assetPath))
{
Debug.LogError("ProjectFolderAssetDataManager SetComment erorr: assetPath is empty");
return;
}
if (!assetPath.StartsWith("Assets"))
{
Debug.LogError("ProjectFolderAssetDataManager SetComment erorr: not Unity project path=" + assetPath);
return;
}
ProjectFolderAssetData findData = null;
var guid = AssetDatabase.AssetPathToGUID(assetPath);
if (!_folderDatas.TryGetValue(guid, out findData))
{
findData = new ProjectFolderAssetData();
findData.assetPath = assetPath;
findData.comment = comment;
_folderDatas.Add(guid, findData);
}
else
{
if (string.IsNullOrEmpty(comment))
_folderDatas.Remove(guid);
else
{
findData.assetPath = assetPath;
findData.comment = comment;
}
}
}
/// <summary>
/// 保存文件夹信息到文件中
/// <param name="path">保存路径</param>
/// </summary>
public void SaveToFile()
{
SaveToFile(SAVE_DATA_PATH);
}
private void SaveToFile(string path)
{
if (_folderDatas.Count == 0)
{
System.IO.File.Delete(path);
return;
}
var saveStr = new System.Text.StringBuilder();
foreach (var iter in _folderDatas)
{
saveStr.Append(iter.Key);
saveStr.Append('\n');
saveStr.Append(iter.Value.assetPath);
saveStr.Append('\n');
saveStr.Append(iter.Value.comment);
saveStr.Append('\n');
}
if (saveStr.Length > 0)
saveStr.Remove(saveStr.Length - 1, 1);
var directoryIath = new System.IO.DirectoryInfo(path).Parent;
if (!directoryIath.Exists)
{
directoryIath.Create();
}
System.IO.File.WriteAllText(path, saveStr.ToString(), System.Text.Encoding.UTF8);
}
/// <summary>
/// 从文件读取文件夹信息
/// <param name="path">保存路径</param>
/// </summary>
private void LoadFromFile(string path)
{
_folderDatas.Clear();
if (!System.IO.File.Exists(path))
return;
try
{
using (var readStream = new System.IO.StreamReader(path))
{
while (true)
{
var guid = readStream.ReadLine();
if (string.IsNullOrEmpty(guid))
break;
var assetPath = readStream.ReadLine();
if (string.IsNullOrEmpty(assetPath))
{
throw new System.Exception("ProjectFolderAssetDataManager LoadFromFile error: not found assetPath, guid=" + guid);
}
var comment = readStream.ReadLine();
if (string.IsNullOrEmpty(comment))
{
throw new System.Exception("ProjectFolderAssetDataManager LoadFromFile error: not found comment, name=" + assetPath);
}
var folderAssestData = new ProjectFolderAssetData();
folderAssestData.assetPath = assetPath;
folderAssestData.comment = comment;
_folderDatas.Add(guid, folderAssestData);
}
readStream.Close();
}
}
catch (System.Exception e)
{
Debug.LogError("ProjectFolderAssetDataManager LoadFromFile exception: e=" + e);
System.IO.File.Delete(path);
}
}
static private GUIStyle _guiStyleLabelTree = null;
static private GUIStyle _guiStyleLabelNotTree = null;
static private void ProjectWindowItemOnGUI(string guid, Rect selectionRect)
{
var folderData = ProjectFolderAssetDataManager.instance.GetFolderData(guid);
if (null == folderData || string.IsNullOrEmpty(folderData.comment))
return;
if (null == _guiStyleLabelTree)
{
_guiStyleLabelTree = EditorStyles.label;
_guiStyleLabelNotTree = EditorStyles.label;
_guiStyleLabelTree.fontSize = 12;
_guiStyleLabelNotTree.fontSize = 10;
}
var aliasContent = new GUIContent(folderData.comment);
var isTree = IsTreeView(selectionRect);
var labelStyle = isTree ? _guiStyleLabelTree : _guiStyleLabelNotTree;
Vector2 labelSize = labelStyle.CalcSize(aliasContent);
float offsetYWhenCurrentSelectAsset = 0;
if (!isTree)
{
IsIconSmall(ref selectionRect);
if (null != Selection.objects)
{
if (null != System.Array.Find(Selection.objects, v => AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(v)) == guid))
offsetYWhenCurrentSelectAsset = -labelSize.y * 0.167f;
}
}
Rect textRect = new Rect(
selectionRect.x + Mathf.Max(0, (selectionRect.width - labelSize.x) * 0.5f),
selectionRect.yMax + (isTree ? -labelSize.y - labelSize.y * 0.167f : labelSize.y * 0.33f - labelSize.y) + offsetYWhenCurrentSelectAsset,
labelSize.x, labelSize.y);
float cropWidth = selectionRect.width;
if (isTree)
{
textRect.width = System.Math.Min(labelSize.x, selectionRect.width / 3);
cropWidth = textRect.width;
textRect.x = selectionRect.xMax - textRect.width;
selectionRect.y = selectionRect.y;
}
aliasContent.text = CropText(labelStyle, aliasContent.text, cropWidth);
EditorGUI.LabelField(textRect, aliasContent, labelStyle);
}
private static bool IsTreeView(Rect rect)
{
return rect.height <= 21f;
}
// https://github.com/PhannGor/unity3d-rainbow-folders/blob/master/Assets/Plugins/RainbowFolders/Editor/Scripts/RainbowFoldersBrowserIcons.cs
private static bool IsIconSmall(ref Rect rect)
{
var isSmall = rect.width > rect.height;
if (isSmall)
rect.width = rect.height;
else
rect.height = rect.width;
return isSmall;
}
static private System.Reflection.MethodInfo _getNumCharactersThatFitWithinWidth;
/// <summary>
/// UnityEditor.ObjectListArea - GetCroppedLabelText (G:1211)
/// </summary>
/// <param name="self"></param>
/// <param name="text"></param>
/// <param name="cropWidth"></param>
/// <param name="symbol"></param>
/// <returns></returns>
public static string CropText(GUIStyle self, string text, in float cropWidth, string symbol = "…")
{
if (null == _getNumCharactersThatFitWithinWidth)
{
_getNumCharactersThatFitWithinWidth = typeof(GUIStyle).GetMethod("GetNumCharactersThatFitWithinWidth",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
}
int thatFitWithinWidth = (int)_getNumCharactersThatFitWithinWidth.Invoke(self, new object[] { text, cropWidth });
int num;
switch (thatFitWithinWidth)
{
case -1:
return text;
case 0:
case 1:
num = 0;
break;
default:
num = thatFitWithinWidth != text.Length ? 1 : 0;
break;
}
text = num == 0 ? text : text.Substring(0, thatFitWithinWidth - 1) + symbol;
return text;
}
}
}