add documentation window

This commit is contained in:
Mikhail 2024-06-10 18:05:06 +08:00
parent 784b743b2a
commit 94dff93393
12 changed files with 700 additions and 12 deletions

8
src/DragonDocs.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1eb7c6f267c708349ad2a1acfd01ebe3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,55 @@
using DCFApixels.DragonECS.PoolsCore;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using UnityEngine;
namespace DCFApixels.DragonECS.Unity.Docs
{
[Serializable]
[DataContract]
public class DragonDocs
{
[DataMember, SerializeField]
private readonly DragonDocsMeta[] _metas;
public ReadOnlySpan<DragonDocsMeta> Metas
{
get { return new ReadOnlySpan<DragonDocsMeta>(_metas); }
}
private DragonDocs(DragonDocsMeta[] metas)
{
_metas = metas;
}
public static DragonDocs Generate()
{
List<DragonDocsMeta> metas = new List<DragonDocsMeta>(256);
foreach (var type in GetTypes())
{
metas.Add(new DragonDocsMeta(type.ToMeta()));
}
DragonDocsMeta[] array = metas.ToArray();
Array.Sort(array);
return new DragonDocs(array);
}
private static List<Type> GetTypes()
{
Type metaAttributeType = typeof(EcsMetaAttribute);
Type memberType = typeof(IEcsMember);
List<Type> result = new List<Type>(512);
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in assembly.GetTypes())
{
if (memberType.IsAssignableFrom(type) || Attribute.GetCustomAttributes(type, metaAttributeType, false).Length > 1)
{
result.Add(type);
}
}
}
return result;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4c0e56cea065aeb49b2aa10ea2a55a77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,105 @@
using System;
using System.Runtime.Serialization;
using UnityEngine;
namespace DCFApixels.DragonECS.Unity.Docs
{
[Serializable]
[DataContract]
public class DragonDocsMeta : IComparable<DragonDocsMeta>
{
[NonSerialized] private Type _sourceType;
[NonSerialized] private bool _isInitSourceType = false;
[DataMember, SerializeField] internal string _assemblyQualifiedName = string.Empty;
[DataMember, SerializeField] internal string _name = string.Empty;
[DataMember, SerializeField] internal bool _isCustomName = false;
[DataMember, SerializeField] internal MetaColor _color = MetaColor.BlackColor;
[DataMember, SerializeField] internal bool _isCustomColor = false;
[DataMember, SerializeField] internal string _autor = string.Empty;
[DataMember, SerializeField] internal string _description = string.Empty;
[DataMember, SerializeField] internal string _group = string.Empty;
[DataMember, SerializeField] internal string[] _tags = Array.Empty<string>();
public string AssemblyQualifiedName
{
get { return _assemblyQualifiedName; }
}
public string Name
{
get { return _name; }
}
public bool IsCustomName
{
get { return _isCustomName; }
}
public MetaColor Color
{
get { return _color; }
}
public bool IsCustomColor
{
get { return _isCustomColor; }
}
public string Autor
{
get { return _autor; }
}
public string Description
{
get { return _description; }
}
public string Group
{
get { return _description; }
}
public ReadOnlySpan<string> Tags
{
get { return _tags; }
}
public DragonDocsMeta(TypeMeta meta)
{
_sourceType = meta.Type;
_assemblyQualifiedName = meta.Type.AssemblyQualifiedName;
_name = meta.Name;
_isCustomName = meta.IsCustomName;
_color = meta.Color;
_isCustomColor = meta.IsCustomColor;
_autor = meta.Description.Author;
_description = meta.Description.Text;
_group = meta.Group.Name;
_tags = new string[meta.Tags.Count];
for (int i = 0, n = meta.Tags.Count; i < n; i++)
{
_tags[i] = meta.Tags[i];
}
}
public bool TryGetSourceType(out Type type)
{
type = GetSourceType();
return type != null;
}
private Type GetSourceType()
{
if (_isInitSourceType) { return _sourceType; }
_isInitSourceType = true;
_sourceType = Type.GetType(_assemblyQualifiedName);
return _sourceType;
}
int IComparable<DragonDocsMeta>.CompareTo(DragonDocsMeta other)
{
// int c = string.Compare(_group, other._group);
// //return c == 0 ? c : string.Compare(_name, other._name);
// return c;
int c = string.Compare(_name, other._name);
return c == 0 ? c : string.Compare(_group, other._group);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a3ec6d51ea63de54b993a0e4b6330e8f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 16e9bdd295352c54ab0d7ea882e152e8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,156 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace DCFApixels.DragonECS.Unity.Docs.Editors
{
[FilePath(EcsConsts.AUTHOR + "/" + EcsConsts.FRAMEWORK_NAME + "/" + nameof(DragonDocsPrefs) + ".prefs", FilePathAttribute.Location.ProjectFolder)]
internal class DragonDocsPrefs : ScriptableSingleton<DragonDocsPrefs>
{
[SerializeField] private DragonDocs _docs;
[SerializeField] private bool[] _isExpands;
[NonSerialized] private bool _isInitInfos = false;
[NonSerialized] private MetaGroupInfo[] _infos = null;
public DragonDocs Docs
{
get { return _docs; }
}
public Span<bool> IsExpands
{
get { return new Span<bool>(_isExpands, 0, _docs.Metas.Length); }
}
public ReadOnlySpan<MetaGroupInfo> Infos
{
get
{
InitInfos();
return new ReadOnlySpan<MetaGroupInfo>(_infos);
}
}
private void InitInfos()
{
if (_isInitInfos) { return; }
ReadOnlySpan<DragonDocsMeta> metas;
if (_docs == null || (metas = _docs.Metas).IsEmpty)
{
_infos = Array.Empty<MetaGroupInfo>();
_isInitInfos = true;
return;
}
string groupPath = metas[0]._group;
int startIndex = 0;
List<MetaGroupInfo> groups = new List<MetaGroupInfo>(128);
for (int i = 1; i < metas.Length; i++)
{
var meta = metas[i];
if (groupPath != meta._group)
{
if (string.IsNullOrEmpty(groupPath))
{
groups.Add(new MetaGroupInfo("<ROOT>", "<ROOT>", startIndex, i - startIndex, 0));
}
else
{
AddInfo(groups, groupPath, startIndex, i - startIndex);
}
groupPath = meta._group;
startIndex = i;
}
}
AddInfo(groups, groupPath, startIndex, metas.Length - startIndex);
_infos = groups.ToArray();
//_isInitInfos = true;
}
private void AddInfo(List<MetaGroupInfo> infos, string path, int startIndex, int length)
{
MetaGroupInfo lastInfo = infos[infos.Count - 1];
if (lastInfo.Depth == 0) { lastInfo = new MetaGroupInfo("", "", 0, 0, 0); }
int depth = 1;
int lastSeparatorIndex = 0;
int i = 0;
int nameLength = 0;
if(lastInfo.Path.Length <= path.Length)
{
for (int j = 0, jMax = lastInfo.Path.Length; j < jMax; j++)
{
char lastChr = lastInfo.Path[j];
char chr = path[j];
if (lastChr == chr)
{
i++;
}
else
{
break;
}
if (chr == '/') // || j == jMax - 1
{
depth++;
lastSeparatorIndex = j + 1;
nameLength = 0;
}
else
{
nameLength++;
}
}
}
for (int iMax = path.Length; i < iMax; i++)
{
char chr = path[i];
if (chr == '/')
{
infos.Add(new MetaGroupInfo(path.Substring(0, lastSeparatorIndex + nameLength + 1), path.Substring(lastSeparatorIndex, nameLength), startIndex, i == iMax - 1 ? length : 0, depth));
depth++;
lastSeparatorIndex = i + 1;
nameLength = 0;
}
else
{
nameLength++;
}
}
}
public void Save(DragonDocs docs)
{
_docs = docs;
if(_isExpands == null || _isExpands.Length != docs.Metas.Length)
{
Array.Resize(ref _isExpands, docs.Metas.Length);
_isExpands[0] = true;
}
_isInitInfos = false;
_infos = null;
Save(true);
}
}
internal struct MetaGroupInfo
{
public readonly string Path;
public readonly string Name;
public readonly int StartIndex;
public readonly int Length;
public readonly int Depth;
public MetaGroupInfo(string path, string name, int startIndex, int length, int depth)
{
Path = path;
Name = name;
StartIndex = startIndex;
Length = length;
Depth = depth;
}
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 988ba077639ea4d42b79ff0605103c88
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,277 @@
#if UNITY_EDITOR
using DCFApixels.DragonECS.Unity.Editors;
using DCFApixels.DragonECS.Unity.Internal;
using UnityEditor;
using UnityEngine;
namespace DCFApixels.DragonECS.Unity.Docs.Editors
{
internal class DragonDocsWindow : EditorWindow
{
[MenuItem("Tools/" + EcsConsts.FRAMEWORK_NAME + "/Documentation")]
static void Open()
{
var wnd = GetWindow<DragonDocsWindow>();
wnd.titleContent = new GUIContent($"{EcsConsts.FRAMEWORK_NAME} Documentation");
wnd.Show();
}
private int _selectedIndex = 0;
private Vector2 ButtonsScrolPosition;
private Vector2 DataScrolPosition;
private Vector2 _buttonsWidthDragStartPos = Vector2.zero;
private float _buttonsWidthDragStartValue = 200f;
private float _buttonsWidth = 200f;
private DragState _dragState;
private enum DragState
{
None,
Init,
Update,
}
private DragonDocsPrefs Prefs { get { return DragonDocsPrefs.instance; } }
private static bool IsShowHidden
{
get { return SettingsPrefs.instance.IsShowHidden; }
set { SettingsPrefs.instance.IsShowHidden = value; }
}
private void OnGUI()
{
Event current = Event.current;
DragonDocs docs = DragonDocsPrefs.instance.Docs;
if(docs == null || docs.Metas.IsEmpty)
{
docs = DragonDocs.Generate();
DragonDocsPrefs.instance.Save(docs);
}
GUILayout.BeginHorizontal(GUILayout.ExpandHeight(true));
ButtonsScrolPosition = GUILayout.BeginScrollView(ButtonsScrolPosition, EditorStyles.helpBox, GUILayout.Width(_buttonsWidth));
var selectedGroupInfo = DrawGroups();
GUILayout.EndScrollView();
DrawDragger();
DataScrolPosition = GUILayout.BeginScrollView(DataScrolPosition, UnityEditorUtility.GetStyle(Color.black, 0.2f), GUILayout.ExpandWidth(true));
DrawSelectedGroupMeta(selectedGroupInfo);
GUILayout.EndScrollView();
GUILayout.EndHorizontal();
GUI.enabled = true;
IsShowHidden = EditorGUILayout.Toggle("Show Hidden", IsShowHidden);
if (GUILayout.Button("Update"))
{
docs = DragonDocs.Generate();
DragonDocsPrefs.instance.Save(docs);
}
}
private void DrawSelectedGroupMeta(MetaGroupInfo info)
{
var metas = Prefs.Docs.Metas;
for (int i = 0, j = info.StartIndex; i < info.Length; i++, j++)
{
DrawMeta(metas[j], i, 12);
}
}
private void DrawMeta(DragonDocsMeta meta, int index, int total)
{
using (EcsGUI.SetIndentLevel(0))
{
Color panelColor = EcsGUI.SelectPanelColor(meta.Color, meta.IsCustomColor, index, total).Desaturate(EscEditorConsts.COMPONENT_DRAWER_DESATURATE);
Color alphaPanelColor = panelColor;
alphaPanelColor.a = EscEditorConsts.COMPONENT_DRAWER_ALPHA;
GUILayout.BeginVertical(UnityEditorUtility.GetStyle(alphaPanelColor));
GUILayout.Space(1f);
GUILayout.TextArea(meta.Name, EditorStyles.boldLabel);
Rect lastRect = GUILayoutUtility.GetLastRect();
if (string.IsNullOrEmpty(meta.Description))
{
using (EcsGUI.SetContentColor(1f, 1f, 1f, 0.4f))
{
Rect pos = lastRect;
pos.xMin = Mathf.Max(EditorGUIUtility.labelWidth, pos.xMax - 42f);
GUI.Label(pos, "empty");
}
}
else
{
Rect lineRect = lastRect;
lineRect.yMin = lineRect.yMax;
lineRect.yMax += 1f;
lineRect.y += 5f;
EditorGUI.DrawRect(lineRect, new Color(1, 1, 1, 0.12f));
GUILayout.Space(7f);
GUILayout.TextArea(meta.Description, EditorStyles.wordWrappedLabel);
}
GUILayout.Space(1f);
GUILayout.EndVertical();
}
}
private MetaGroupInfo DrawGroups()
{
using (EcsGUI.SetIndentLevel(0))
{
Event current = Event.current;
MetaGroupInfo result = new MetaGroupInfo("NO_NAME", "NO_NAME", 0, 0, 0);
var infos = Prefs.Infos;
var IsExpands = Prefs.IsExpands;
int clippingDepth = int.MaxValue;
for (int i = 0; i < infos.Length; i++)
{
var groupInfo = infos[i];
if (groupInfo.Depth > clippingDepth)
{
continue;
}
else
{
clippingDepth = int.MaxValue;
}
EditorGUI.indentLevel = groupInfo.Depth;
GUIContent label = UnityEditorUtility.GetLabel(groupInfo.Name);
Rect r = GUILayoutUtility.GetRect(label, EditorStyles.foldout);
if (i == _selectedIndex)
{
EditorGUI.DrawRect(r, new Color(0.12f, 0.5f, 1f, 0.40f));
result = groupInfo;
}
bool isClick;
using (EcsGUI.SetColor(0, 0, 0, 0))
using (EcsGUI.Disable)
{
isClick = GUI.Button(r, "");
}
if (EcsGUI.HitTest(r))
{
EditorGUI.DrawRect(r, new Color(1f, 1f, 1f, 0.12f));
if (current.type == EventType.MouseDown)
{
_selectedIndex = i;
}
}
if (i + 1 == infos.Length || infos[i + 1].Depth <= groupInfo.Depth)
{
using (EcsGUI.SetBackgroundColor(0, 0, 0, 0))
EditorGUI.Foldout(r, false, label, EditorStyles.foldout);
}
else
{
IsExpands[i] = EditorGUI.Foldout(r, IsExpands[i], label, EditorStyles.foldout);
if (i == 0)
{
IsExpands[i] = true;
}
}
if (IsExpands[i] == false)
{
clippingDepth = groupInfo.Depth;
}
if (groupInfo.Length > 0)
{
r.xMax = r.xMin;
r.xMin -= 2f;
r.yMin += 1;
r.yMax -= 1;
EditorGUI.DrawRect(r, new Color(0.2f, 0.6f, 1f));
}
}
return result;
}
}
private void DrawDragger()
{
const float DRAG_RESIZE_WIDTH = 4f;
Rect rect;
float m = DRAG_RESIZE_WIDTH;
if (_dragState != DragState.None)
{
m *= 200f;
}
rect = GUILayoutUtility.GetLastRect();
rect.xMin = rect.xMax;
rect.xMax = rect.xMax + m;
Event current = Event.current;
switch (current.type)
{
case EventType.MouseDown:
if (EcsGUI.HitTest(rect))
{
_buttonsWidthDragStartPos = current.mousePosition;
_buttonsWidthDragStartValue = _buttonsWidth;
_dragState = DragState.Init;
}
break;
case EventType.MouseUp:
_dragState = DragState.None;
current.Use();
break;
case EventType.MouseDrag:
{
switch (_dragState)
{
case DragState.Init:
{
if ((Event.current.mousePosition - _buttonsWidthDragStartPos).sqrMagnitude > 16f)
{
_dragState = DragState.Update;
}
}
break;
case DragState.Update:
{
_buttonsWidth = _buttonsWidthDragStartValue + (Event.current.mousePosition.x - _buttonsWidthDragStartPos.x);
_buttonsWidth = Mathf.Max(6f, _buttonsWidth);
current.Use();//TODO кажется это можно использовать вместо лайфахака с кнопкой для моментальной реакции пир наведении курсора для кнопок с иконками
}
break;
}
}
break;
case EventType.Repaint:
{
EditorGUIUtility.AddCursorRect(rect, MouseCursor.SlideArrow);
}
break;
}
}
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: db40c55995fb85b47bffaf33fb4d337c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -37,7 +37,7 @@ namespace DCFApixels.DragonECS
public virtual MetaColor Color { get { return new MetaColor(MetaColor.Black); } } public virtual MetaColor Color { get { return new MetaColor(MetaColor.Black); } }
public virtual MetaGroup Group { get { return MetaGroup.Empty; } } public virtual MetaGroup Group { get { return MetaGroup.Empty; } }
public virtual MetaDescription Description { get { return MetaDescription.Empty; } } public virtual MetaDescription Description { get { return MetaDescription.Empty; } }
public virtual IReadOnlyCollection<string> Tags { get { return Array.Empty<string>(); } } public virtual IReadOnlyList<string> Tags { get { return Array.Empty<string>(); } }
#endregion #endregion
#region Methods #region Methods
@ -62,7 +62,7 @@ namespace DCFApixels.DragonECS
public override MetaColor Color { get { return Meta.Color; } } public override MetaColor Color { get { return Meta.Color; } }
public override MetaGroup Group { get { return Meta.Group; } } public override MetaGroup Group { get { return Meta.Group; } }
public override MetaDescription Description { get { return Meta.Description; } } public override MetaDescription Description { get { return Meta.Description; } }
public override IReadOnlyCollection<string> Tags { get { return Meta.Tags; } } public override IReadOnlyList<string> Tags { get { return Meta.Tags; } }
#endregion #endregion
#region Methods #region Methods

View File

@ -53,13 +53,43 @@ namespace DCFApixels.DragonECS.Unity.Editors
GUI.contentColor = _value; GUI.contentColor = _value;
} }
} }
private static ContentColorScope SetContentColor(Color value) => new ContentColorScope(value); public struct BackgroundColorScope : IDisposable
private static ContentColorScope SetContentColor(float r, float g, float b, float a = 1f) => new ContentColorScope(r, g, b, a); {
private static ColorScope SetColor(Color value) => new ColorScope(value); private readonly Color _value;
private static ColorScope SetColor(float r, float g, float b, float a = 1f) => new ColorScope(r, g, b, a); public BackgroundColorScope(float r, float g, float b, float a = 1f) : this(new Color(r, g, b, a)) { }
private static EditorGUI.DisabledScope Enable => new EditorGUI.DisabledScope(false); public BackgroundColorScope(Color value)
private static EditorGUI.DisabledScope Disable => new EditorGUI.DisabledScope(true); {
private static EditorGUI.DisabledScope SetEnable(bool value) => new EditorGUI.DisabledScope(!value); _value = GUI.backgroundColor;
GUI.backgroundColor = value;
}
public void Dispose()
{
GUI.backgroundColor = _value;
}
}
public struct IndentLevelScope : IDisposable
{
private readonly int _value;
public IndentLevelScope(int value)
{
_value = EditorGUI.indentLevel;
EditorGUI.indentLevel = value;
}
public void Dispose()
{
EditorGUI.indentLevel = _value;
}
}
public static IndentLevelScope SetIndentLevel(int level) => new IndentLevelScope(level);
public static ContentColorScope SetContentColor(Color value) => new ContentColorScope(value);
public static ContentColorScope SetContentColor(float r, float g, float b, float a = 1f) => new ContentColorScope(r, g, b, a);
public static BackgroundColorScope SetBackgroundColor(Color value) => new BackgroundColorScope(value);
public static BackgroundColorScope SetBackgroundColor(float r, float g, float b, float a = 1f) => new BackgroundColorScope(r, g, b, a);
public static ColorScope SetColor(Color value) => new ColorScope(value);
public static ColorScope SetColor(float r, float g, float b, float a = 1f) => new ColorScope(r, g, b, a);
public static EditorGUI.DisabledScope Enable => new EditorGUI.DisabledScope(false);
public static EditorGUI.DisabledScope Disable => new EditorGUI.DisabledScope(true);
public static EditorGUI.DisabledScope SetEnable(bool value) => new EditorGUI.DisabledScope(!value);
#endregion #endregion
private static readonly BindingFlags fieldFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly BindingFlags fieldFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
@ -295,16 +325,21 @@ namespace DCFApixels.DragonECS.Unity.Editors
public static Color SelectPanelColor(ITypeMeta meta, int index, int total) public static Color SelectPanelColor(ITypeMeta meta, int index, int total)
{ {
var trueMeta = meta.Type.ToMeta(); var trueMeta = meta.Type.ToMeta();
if (trueMeta.IsCustomColor || meta.Color != trueMeta.Color) bool isCustomColor = trueMeta.IsCustomColor || meta.Color != trueMeta.Color;
return SelectPanelColor(meta.Color, isCustomColor, index, total);
}
public static Color SelectPanelColor(MetaColor color, bool isCustomColor, int index, int total)
{
if (isCustomColor)
{ {
return meta.Color.ToUnityColor(); return color.ToUnityColor();
} }
else else
{ {
switch (AutoColorMode) switch (AutoColorMode)
{ {
case ComponentColorMode.Auto: case ComponentColorMode.Auto:
return meta.Color.ToUnityColor().Desaturate(0.48f) / 1.18f; //.Desaturate(0.48f) / 1.18f; return color.ToUnityColor().Desaturate(0.48f) / 1.18f; //.Desaturate(0.48f) / 1.18f;
case ComponentColorMode.Rainbow: case ComponentColorMode.Rainbow:
int localTotal = Mathf.Max(total, EscEditorConsts.AUTO_COLOR_RAINBOW_MIN_RANGE); int localTotal = Mathf.Max(total, EscEditorConsts.AUTO_COLOR_RAINBOW_MIN_RANGE);
Color hsv = Color.HSVToRGB(1f / localTotal * (index % localTotal), 1, 1); Color hsv = Color.HSVToRGB(1f / localTotal * (index % localTotal), 1, 1);