Initial commit: Unity WordConnect project

This commit is contained in:
2025-08-01 19:12:05 +08:00
commit f14db75802
3503 changed files with 448337 additions and 0 deletions

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c9d3500c8eb84e07acfb1cb574d9f9e2
timeCreated: 1632057810

View File

@ -0,0 +1,81 @@
using System;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace VContainer.Editor.Diagnostics
{
// reflection call of UnityEditor.SplitterGUILayout
static class SplitterGUILayout
{
static BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
static readonly Lazy<Type> SplitterStateType = new Lazy<Type>(() =>
{
var type = typeof(EditorWindow).Assembly.GetTypes().First(x => x.FullName == "UnityEditor.SplitterState");
return type;
});
static readonly Lazy<ConstructorInfo> SplitterStateCtor = new Lazy<ConstructorInfo>(() =>
{
var type = SplitterStateType.Value;
return type.GetConstructor(flags, null, new Type[] { typeof(float[]), typeof(int[]), typeof(int[]) }, null);
});
static readonly Lazy<Type> SplitterGUILayoutType = new Lazy<Type>(() =>
{
var type = typeof(EditorWindow).Assembly.GetTypes().First(x => x.FullName == "UnityEditor.SplitterGUILayout");
return type;
});
static readonly Lazy<MethodInfo> BeginVerticalSplitInfo = new Lazy<MethodInfo>(() =>
{
var type = SplitterGUILayoutType.Value;
return type.GetMethod("BeginVerticalSplit", flags, null, new Type[] { SplitterStateType.Value, typeof(GUILayoutOption[]) }, null);
});
static readonly Lazy<MethodInfo> EndVerticalSplitInfo = new Lazy<MethodInfo>(() =>
{
var type = SplitterGUILayoutType.Value;
return type.GetMethod("EndVerticalSplit", flags, null, Type.EmptyTypes, null);
});
static readonly Lazy<MethodInfo> BeginHorizontalSplitInfo = new Lazy<MethodInfo>(() =>
{
var type = SplitterGUILayoutType.Value;
return type.GetMethod("BeginHorizontalSplit", flags, null, new Type[] { SplitterStateType.Value, typeof(GUILayoutOption[]) }, null);
});
static readonly Lazy<MethodInfo> EndHorizontalSplitInfo = new Lazy<MethodInfo>(() =>
{
var type = SplitterGUILayoutType.Value;
return type.GetMethod("EndHorizontalSplit", flags, null, Type.EmptyTypes, null);
});
public static object CreateSplitterState(float[] relativeSizes, int[] minSizes, int[] maxSizes)
{
return SplitterStateCtor.Value.Invoke(new object[] { relativeSizes, minSizes, maxSizes });
}
public static void BeginVerticalSplit(object splitterState, params GUILayoutOption[] options)
{
BeginVerticalSplitInfo.Value.Invoke(null, new object[] { splitterState, options });
}
public static void EndVerticalSplit()
{
EndVerticalSplitInfo.Value.Invoke(null, Array.Empty<object>());
}
public static void BeginHorizontalSplit(object splitterState, params GUILayoutOption[] options)
{
BeginHorizontalSplitInfo.Value.Invoke(null, new object[] { splitterState, options });
}
public static void EndHorizontalSplit()
{
EndHorizontalSplitInfo.Value.Invoke(null, Array.Empty<object>());
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: af963129cd84433ba74949eeb1c00542
timeCreated: 1633102883

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace VContainer.Editor.Diagnostics
{
static class TypeNameHelper
{
static readonly IReadOnlyDictionary<Type, string> TypeAlias = new Dictionary<Type, string>
{
{ typeof(bool), "bool" },
{ typeof(byte), "byte" },
{ typeof(char), "char" },
{ typeof(decimal), "decimal" },
{ typeof(double), "double" },
{ typeof(float), "float" },
{ typeof(int), "int" },
{ typeof(long), "long" },
{ typeof(object), "object" },
{ typeof(sbyte), "sbyte" },
{ typeof(short), "short" },
{ typeof(string), "string" },
{ typeof(uint), "uint" },
{ typeof(ulong), "ulong" },
{ typeof(void), "void" }
};
public static string GetTypeAlias(Type type)
{
if (TypeAlias.TryGetValue(type, out var alias))
{
return alias;
}
if (type.IsGenericType)
{
var typeParameters = type.GetGenericArguments().Select(GetTypeAlias);
var typeNameBase = type.Name.Split('`')[0];
return $"{typeNameBase}<{string.Join(",", typeParameters)}>";
}
return type.Name;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 639563c4569244d8a54a150c42c8de33
timeCreated: 1633621836

View File

@ -0,0 +1,323 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System;
using System.Linq;
using UnityEditor.IMGUI.Controls;
using VContainer.Diagnostics;
using VContainer.Unity;
namespace VContainer.Editor.Diagnostics
{
public sealed class DiagnosticsInfoTreeViewItem : TreeViewItem
{
public string ScopeName { get; set; }
public DiagnosticsInfo DiagnosticsInfo { get; }
public RegistrationBuilder RegistrationBuilder => DiagnosticsInfo.RegisterInfo.RegistrationBuilder;
public Registration Registration => DiagnosticsInfo.ResolveInfo.Registration;
public int? RefCount => DiagnosticsInfo.ResolveInfo.RefCount;
public long ResolveTime => DiagnosticsInfo.ResolveInfo.ResolveTime;
public string TypeSummary => TypeNameHelper.GetTypeAlias(Registration.ImplementationType);
public string ContractTypesSummary
{
get
{
if (Registration.InterfaceTypes != null)
{
var values = Registration.InterfaceTypes.Select(TypeNameHelper.GetTypeAlias);
return string.Join(", ", values);
}
return "";
}
}
public string RegisterSummary
{
get
{
if (RegistrationBuilder == null)
return "";
var type = RegistrationBuilder.GetType();
if (type == typeof(RegistrationBuilder))
{
return "";
}
var typeName = type.Name;
var suffixIndex = typeName.IndexOf("Builder");
if (suffixIndex > 0)
{
typeName = typeName.Substring(0, suffixIndex);
}
suffixIndex = typeName.IndexOf("Registration");
if (suffixIndex > 0)
{
typeName = typeName.Substring(0, suffixIndex);
}
if (typeName.StartsWith("Instance") && TypeSummary.StartsWith("Func<"))
{
return "FuncFactory";
}
return typeName;
}
}
public DiagnosticsInfoTreeViewItem(DiagnosticsInfo info)
{
ScopeName = info.ScopeName;
DiagnosticsInfo = info;
displayName = TypeSummary;
}
}
public sealed class VContainerDiagnosticsInfoTreeView : TreeView
{
static readonly MultiColumnHeaderState.Column[] Columns =
{
new MultiColumnHeaderState.Column { headerContent = new GUIContent("Type") },
new MultiColumnHeaderState.Column { headerContent = new GUIContent("ContractTypes"), canSort = false },
new MultiColumnHeaderState.Column { headerContent = new GUIContent("Lifetime"), width = 15f },
new MultiColumnHeaderState.Column { headerContent = new GUIContent("Register"), width = 15f },
new MultiColumnHeaderState.Column { headerContent = new GUIContent("RefCount"), width = 5f },
new MultiColumnHeaderState.Column { headerContent = new GUIContent("Scope"), width = 20f },
new MultiColumnHeaderState.Column { headerContent = new GUIContent("Time"), width = 20f },
};
static int idSeed;
static int NextId() => ++idSeed;
const string SessionStateKeySortedColumnIndex = "VContainer.Editor.DiagnosticsInfoTreeView:sortedColumnIndex";
public bool Flatten
{
get => flatten;
set
{
flatten = value;
multiColumnHeader.ResizeToFit();
}
}
bool flatten;
public VContainerDiagnosticsInfoTreeView()
: this(new TreeViewState(), new MultiColumnHeader(new MultiColumnHeaderState(Columns)))
{
}
VContainerDiagnosticsInfoTreeView(TreeViewState state, MultiColumnHeader header)
: base(state, header)
{
rowHeight = 20;
showAlternatingRowBackgrounds = true;
showBorder = true;
header.sortingChanged += OnSortedChanged;
header.ResizeToFit();
Reload();
header.sortedColumnIndex = Math.Min(
header.state.columns.Length - 1,
SessionState.GetInt(SessionStateKeySortedColumnIndex, 0));
}
public DiagnosticsInfoTreeViewItem GetSelectedItem()
{
if (state.selectedIDs.Count <= 0) return null;
var selectedId = state.selectedIDs[0];
return GetRows().FirstOrDefault(x => x.id == selectedId) as DiagnosticsInfoTreeViewItem;
}
public void ReloadAndSort()
{
var currentSelected = state.selectedIDs;
Reload();
OnSortedChanged(multiColumnHeader);
state.selectedIDs = currentSelected;
}
void OnSortedChanged(MultiColumnHeader multiColumnHeader)
{
var columnIndex = multiColumnHeader.sortedColumnIndex;
if (columnIndex < 0) return;
SessionState.SetInt(SessionStateKeySortedColumnIndex, columnIndex);
var ascending = multiColumnHeader.IsSortedAscending(columnIndex);
if (Flatten)
{
var items = rootItem.children.Cast<DiagnosticsInfoTreeViewItem>();
rootItem.children = new List<TreeViewItem>(Sort(items, columnIndex, ascending));
}
else
{
foreach (var sectionHeaderItem in rootItem.children)
{
var items = sectionHeaderItem.children.Cast<DiagnosticsInfoTreeViewItem>();
sectionHeaderItem.children = new List<TreeViewItem>(Sort(items, columnIndex, ascending));
}
}
BuildRows(rootItem);
}
protected override TreeViewItem BuildRoot()
{
var root = new TreeViewItem { depth = -1 };
var children = new List<TreeViewItem>();
if (VContainerSettings.DiagnosticsEnabled)
{
if (Flatten)
{
var infos = DiagnositcsContext.GetDiagnosticsInfos();
foreach (var info in infos)
{
children.Add(new DiagnosticsInfoTreeViewItem(info)
{
id = NextId(),
depth = 0,
ScopeName = info.ScopeName,
});
}
}
else
{
var grouped = DiagnositcsContext.GetGroupedDiagnosticsInfos();
foreach (var scope in grouped)
{
var sectionHeaderItem = new TreeViewItem(NextId(), 0, scope.Key);
children.Add(sectionHeaderItem);
SetExpanded(sectionHeaderItem.id, true);
foreach (var info in scope)
{
AddDependencyItemsRecursive(info, sectionHeaderItem);
}
}
}
}
root.children = children;
return root;
}
protected override bool CanMultiSelect(TreeViewItem item) => false;
protected override void RowGUI(RowGUIArgs args)
{
var item = args.item as DiagnosticsInfoTreeViewItem;
if (item is null)
{
var cellRect = args.GetCellRect(0);
GUI.BeginGroup(cellRect);
{
args.rowRect = new Rect(0, 0, cellRect.width, cellRect.height);
base.RowGUI(args);
}
GUI.EndGroup();
return;
}
for (var visibleColumnIndex = 0; visibleColumnIndex < args.GetNumVisibleColumns(); visibleColumnIndex++)
{
var cellRect = args.GetCellRect(visibleColumnIndex);
// CenterRectUsingSingleLineHeight(ref cellRect);
var columnIndex = args.GetColumn(visibleColumnIndex);
var labelStyle = args.selected ? EditorStyles.whiteLabel : EditorStyles.label;
labelStyle.alignment = TextAnchor.MiddleLeft;
switch (columnIndex)
{
case 0:
GUI.BeginGroup(cellRect);
{
args.rowRect = new Rect(0, 0, cellRect.width, cellRect.height);
base.RowGUI(args);
}
GUI.EndGroup();
break;
case 1:
EditorGUI.LabelField(cellRect, item.ContractTypesSummary, labelStyle);
break;
case 2:
EditorGUI.LabelField(cellRect, item.Registration.Lifetime.ToString(), labelStyle);
break;
case 3:
EditorGUI.LabelField(cellRect, item.RegisterSummary, labelStyle);
break;
case 4:
EditorGUI.LabelField(cellRect, item.RefCount.ToString(), labelStyle);
break;
case 5:
EditorGUI.LabelField(cellRect, item.ScopeName, labelStyle);
break;
case 6:
EditorGUI.LabelField(cellRect, item.ResolveTime.ToString(), labelStyle);
break;
default:
throw new ArgumentOutOfRangeException(nameof(columnIndex), columnIndex, null);
}
}
}
void AddDependencyItemsRecursive(DiagnosticsInfo info, TreeViewItem parent)
{
var item = new DiagnosticsInfoTreeViewItem(info)
{
id = NextId(),
depth = parent.depth + 1
};
parent.AddChild(item);
SetExpanded(item.id, item.depth <= 1);
foreach (var dependency in info.Dependencies)
{
AddDependencyItemsRecursive(dependency, item);
}
}
IEnumerable<DiagnosticsInfoTreeViewItem> Sort(
IEnumerable<DiagnosticsInfoTreeViewItem> items,
int sortedColumnIndex,
bool ascending)
{
switch (sortedColumnIndex)
{
case 0:
return ascending
? items.OrderBy(x => x.TypeSummary)
: items.OrderByDescending(x => x.TypeSummary);
case 2:
return ascending
? items.OrderBy(x => x.Registration.Lifetime)
: items.OrderByDescending(x => x.Registration.Lifetime);
case 3:
return ascending
? items.OrderBy(x => x.RegisterSummary)
: items.OrderByDescending(x => x.RegisterSummary);
case 4:
return ascending
? items.OrderBy(x => x.RefCount)
: items.OrderByDescending(x => x.RefCount);
case 5:
return ascending
? items.OrderBy(x => x.ScopeName)
: items.OrderByDescending(x => x.ScopeName);
case 6:
return ascending
? items.OrderBy(x => x.ResolveTime)
: items.OrderByDescending(x => x.ResolveTime);
default:
throw new ArgumentOutOfRangeException(nameof(sortedColumnIndex), sortedColumnIndex, null);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0d65b5790cc048599cb6e65bb859e6aa
timeCreated: 1632058224

View File

@ -0,0 +1,216 @@
using System;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using VContainer.Diagnostics;
using VContainer.Unity;
namespace VContainer.Editor.Diagnostics
{
public sealed class VContainerDiagnosticsWindow : EditorWindow
{
static VContainerDiagnosticsWindow window;
static readonly GUIContent FlattenHeadContent = EditorGUIUtility.TrTextContent("Flatten", "Flatten dependencies");
static readonly GUIContent ReloadHeadContent = EditorGUIUtility.TrTextContent("Reload", "Reload View");
internal static bool EnableAutoReload;
internal static bool EnableCaptureStackTrace;
[MenuItem("Window/VContainer Diagnostics")]
public static void OpenWindow()
{
if (window != null)
{
window.Close();
}
GetWindow<VContainerDiagnosticsWindow>("VContainer Diagnostics").Show();
}
GUIStyle TableListStyle
{
get
{
var style = new GUIStyle("CN Box");
style.margin.top = 0;
style.padding.left = 3;
return style;
}
}
GUIStyle DetailsStyle
{
get
{
var detailsStyle = new GUIStyle("CN Message");
detailsStyle.wordWrap = false;
detailsStyle.stretchHeight = true;
detailsStyle.margin.right = 15;
return detailsStyle;
}
}
VContainerDiagnosticsInfoTreeView treeView;
VContainerInstanceTreeView instanceTreeView;
SearchField searchField;
object verticalSplitterState;
object horizontalSplitterState;
Vector2 tableScrollPosition;
Vector2 detailsScrollPosition;
Vector2 instanceScrollPosition;
public void Reload(IObjectResolver resolver)
{
treeView.ReloadAndSort();
Repaint();
}
void OnPlayModeStateChange(PlayModeStateChange state)
{
treeView.ReloadAndSort();
Repaint();
}
void OnEnable()
{
window = this; // set singleton.
verticalSplitterState = SplitterGUILayout.CreateSplitterState(new [] { 75f, 25f }, new [] { 32, 32 }, null);
horizontalSplitterState = SplitterGUILayout.CreateSplitterState(new[] { 75, 25f }, new[] { 32, 32 }, null);
treeView = new VContainerDiagnosticsInfoTreeView();
instanceTreeView = new VContainerInstanceTreeView();
searchField = new SearchField();
DiagnositcsContext.OnContainerBuilt += Reload;
EditorApplication.playModeStateChanged += OnPlayModeStateChange;
}
void OnDisable()
{
DiagnositcsContext.OnContainerBuilt -= Reload;
EditorApplication.playModeStateChanged -= OnPlayModeStateChange;
}
void OnGUI()
{
RenderHeadPanel();
SplitterGUILayout.BeginVerticalSplit(verticalSplitterState, Array.Empty<GUILayoutOption>());
{
SplitterGUILayout.BeginHorizontalSplit(horizontalSplitterState);
{
RenderBuildPanel();
RenderInstancePanel();
}
SplitterGUILayout.EndHorizontalSplit();
RenderStackTracePanel();
}
SplitterGUILayout.EndVerticalSplit();
}
void RenderHeadPanel()
{
using (new EditorGUILayout.VerticalScope())
using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar))
{
var flattenOn = GUILayout.Toggle(treeView.Flatten, FlattenHeadContent, EditorStyles.toolbarButton);
if (flattenOn != treeView.Flatten)
{
treeView.Flatten = flattenOn;
treeView.ReloadAndSort();
Repaint();
}
GUILayout.FlexibleSpace();
treeView.searchString = searchField.OnToolbarGUI(treeView.searchString);
if (GUILayout.Button(ReloadHeadContent, EditorStyles.toolbarButton))
{
treeView.ReloadAndSort();
Repaint();
}
}
}
void RenderBuildPanel()
{
using (new EditorGUILayout.VerticalScope(TableListStyle))
using (var scrollViewScope = new EditorGUILayout.ScrollViewScope(tableScrollPosition,
// true,
// true,
GUILayout.ExpandWidth(true),
GUILayout.MaxWidth(2000f)))
{
tableScrollPosition = scrollViewScope.scrollPosition;
var controlRect = EditorGUILayout.GetControlRect(
GUILayout.ExpandHeight(true),
GUILayout.ExpandWidth(true));
treeView?.OnGUI(controlRect);
}
}
void RenderInstancePanel()
{
if (!VContainerSettings.DiagnosticsEnabled)
{
return;
}
var selectedItem = treeView.GetSelectedItem();
if (selectedItem?.DiagnosticsInfo.ResolveInfo is ResolveInfo resolveInfo)
{
if (resolveInfo.Instances.Count > 0)
{
instanceTreeView.CurrentDiagnosticsInfo = selectedItem.DiagnosticsInfo;
instanceTreeView.Reload();
using (var scrollViewScope = new EditorGUILayout.ScrollViewScope(instanceScrollPosition, GUILayout.ExpandHeight(true)))
{
instanceScrollPosition = scrollViewScope.scrollPosition;
var controlRect = EditorGUILayout.GetControlRect(
GUILayout.ExpandHeight(true),
GUILayout.ExpandWidth(true));
instanceTreeView?.OnGUI(controlRect);
}
}
else
{
EditorGUILayout.SelectableLabel("No instance reference");
}
}
}
void RenderStackTracePanel()
{
var message = "";
if (VContainerSettings.DiagnosticsEnabled)
{
var selectedItem = treeView.GetSelectedItem();
if (selectedItem?.DiagnosticsInfo?.RegisterInfo is RegisterInfo registerInfo)
{
message = $"<a href=\"{registerInfo.GetScriptAssetPath()}\" line=\"{registerInfo.GetFileLineNumber()}\">Register at {registerInfo.GetHeadline()}</a>" +
Environment.NewLine +
Environment.NewLine +
selectedItem.DiagnosticsInfo.RegisterInfo.StackTrace;
}
}
else
{
message = "VContainer Diagnostics collector is disabled. To enable, please check VContainerSettings.";
}
using (var scrollViewScope = new EditorGUILayout.ScrollViewScope(detailsScrollPosition))
{
detailsScrollPosition = scrollViewScope.scrollPosition;
var vector = DetailsStyle.CalcSize(new GUIContent(message));
EditorGUILayout.SelectableLabel(message, DetailsStyle,
GUILayout.ExpandHeight(true),
GUILayout.ExpandWidth(true),
GUILayout.MinWidth(vector.x),
GUILayout.MinHeight(vector.y));
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e3e4c1066bed4185a96639d5b1017922
timeCreated: 1632057811

View File

@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using VContainer.Diagnostics;
namespace VContainer.Editor.Diagnostics
{
public sealed class VContainerInstanceTreeView : TreeView
{
static int idSeed;
static int NextId() => ++idSeed;
static string Stringify(object instance)
{
if (ReferenceEquals(instance, null))
return "null";
if (instance is UnityEngine.Object obj && obj == null)
return "null or destroyed";
return instance.ToString();
}
public DiagnosticsInfo CurrentDiagnosticsInfo { get; set; }
public VContainerInstanceTreeView() : base(new TreeViewState())
{
Reload();
}
protected override TreeViewItem BuildRoot()
{
var root = new TreeViewItem(NextId(), -1, "Root");
var children = new List<TreeViewItem>();
if (CurrentDiagnosticsInfo is DiagnosticsInfo info)
{
for (var i = 0; i < info.ResolveInfo.Instances.Count; i++)
{
var instance = info.ResolveInfo.Instances[i];
var item = new TreeViewItem(NextId(), 0,
$"({TypeNameHelper.GetTypeAlias(instance.GetType())}) {Stringify(instance)}");
AddProperties(instance, item);
children.Add(item);
SetExpanded(item.id, true);
}
}
root.children = children;
return root;
}
void AddProperties(object instance, TreeViewItem parent)
{
if (instance == null) return;
var type = instance.GetType();
var props = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var prop in props)
{
if (prop.PropertyType.IsSubclassOf(typeof(UnityEngine.Object)) &&
prop.IsDefined(typeof(ObsoleteAttribute), true))
{
continue;
}
try
{
if (prop.CanRead)
{
var value = prop.GetValue(instance);
var displayName = $"{prop.Name} = ({TypeNameHelper.GetTypeAlias(prop.PropertyType)}) {Stringify(value)}";
parent.AddChild(new TreeViewItem(NextId(), parent.depth + 1, displayName));
}
else
{
var displayName = $"{prop.Name} = (write-only {TypeNameHelper.GetTypeAlias(prop.PropertyType)})";
parent.AddChild(new TreeViewItem(NextId(), parent.depth + 1, displayName));
}
}
catch (MissingReferenceException)
{
}
catch (NotSupportedException)
{
}
}
foreach (var field in fields)
{
if ((field.FieldType.IsSubclassOf(typeof(UnityEngine.Object)) &&
field.IsDefined(typeof(ObsoleteAttribute), true)) ||
field.IsDefined(typeof(CompilerGeneratedAttribute), true))
{
continue;
}
try
{
var value = field.GetValue(instance);
var displayName = $"{field.Name} = ({TypeNameHelper.GetTypeAlias(field.FieldType)}) {Stringify(value)}";
parent.AddChild(new TreeViewItem(NextId(), parent.depth + 1, displayName));
}
catch (MissingReferenceException)
{
}
catch (NotSupportedException)
{
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 538613b38d4a4facb28765eb06374863
timeCreated: 1633569303

View File

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using VContainer.Unity;
namespace VContainer.Editor
{
[CustomPropertyDrawer(typeof(ParentReference))]
public sealed class ParentReferencePropertyDrawer : PropertyDrawer
{
static string[] GetAllTypeNames()
{
return new List<string> { "None" }
.Concat(TypeCache.GetTypesDerivedFrom<LifetimeScope>()
.Select(type => type.FullName))
.ToArray();
}
static string GetLabel(Type type) => $"{type.Namespace}/{type.Name}";
string[] names;
public override void OnGUI(Rect rect, SerializedProperty prop, GUIContent label)
{
if (names == null)
{
names = GetAllTypeNames();
if (prop.serializedObject.targetObject is LifetimeScope lifetimeScope)
{
var lifetimeScopeName = lifetimeScope.GetType().FullName;
names = names.Where(name => name != lifetimeScopeName).ToArray();
}
}
var typeNameProp = prop.FindPropertyRelative("TypeName");
using (new EditorGUI.PropertyScope(rect, label, prop))
{
var labelRect = new Rect(rect.x, rect.y, rect.width, 18f);
var popupRect = new Rect(rect.x, rect.y + labelRect.height, rect.width , 18f);
var index = Array.IndexOf(names, typeNameProp.stringValue);
if (index < 0) index = 0;
EditorGUI.LabelField(labelRect, "Parent");
using (var check = new EditorGUI.ChangeCheckScope())
{
index = EditorGUI.Popup(popupRect, index, names);
if (check.changed)
{
typeNameProp.stringValue = names[index];
}
}
}
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return 18f + 18f + 4f;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 51521f95bbd442b08bc2b53efcdeff29
timeCreated: 1594556689

View File

@ -0,0 +1,113 @@
using System.IO;
using UnityEditor;
using UnityEngine;
using VContainer.Unity;
#if UNITY_2020_2_OR_NEWER
using UnityEditor.Compilation;
#endif
namespace VContainer.Editor
{
public sealed class ScriptTemplateProcessor : UnityEditor.AssetModificationProcessor
{
#if UNITY_2020_2_OR_NEWER
const string RootNamespaceBeginTag = "#ROOTNAMESPACEBEGIN#";
const string RootNamespaceEndTag = "#ROOTNAMESPACEEND#";
#endif
const string MonoInstallerTemplate =
"using VContainer;\n" +
"using VContainer.Unity;\n" +
"\n" +
#if UNITY_2020_2_OR_NEWER
RootNamespaceBeginTag + "\n" +
#endif
"public class #SCRIPTNAME# : LifetimeScope\n" +
"{\n" +
" protected override void Configure(IContainerBuilder builder)\n" +
" {\n" +
" }\n" +
"}\n" +
#if UNITY_2020_2_OR_NEWER
RootNamespaceEndTag + "\n" +
#endif
"";
public static void OnWillCreateAsset(string metaPath)
{
if (VContainerSettings.Instance != null && VContainerSettings.Instance.DisableScriptModifier)
{
return;
}
var suffixIndex = metaPath.LastIndexOf(".meta");
if (suffixIndex < 0)
{
return;
}
var scriptPath = metaPath.Substring(0, suffixIndex);
var basename = Path.GetFileNameWithoutExtension(scriptPath);
var extname = Path.GetExtension(scriptPath);
if (extname != ".cs")
{
return;
}
if (!scriptPath.EndsWith("LifetimeScope.cs"))
{
return;
}
var content = MonoInstallerTemplate.Replace("#SCRIPTNAME#", basename);
#if UNITY_2020_2_OR_NEWER
{
var rootNamespace = CompilationPipeline.GetAssemblyRootNamespaceFromScriptPath(scriptPath);
content = RemoveOrInsertNamespaceSimple(content, rootNamespace);
}
#endif
if (scriptPath.StartsWith("Assets/"))
{
scriptPath = scriptPath.Substring("Assets/".Length);
}
var fullPath = Path.Combine(Application.dataPath, scriptPath);
File.WriteAllText(fullPath, content);
AssetDatabase.Refresh();
}
#if UNITY_2020_2_OR_NEWER
// https://github.com/Unity-Technologies/UnityCsReference/blob/2020.2/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs#L495-L550
static string RemoveOrInsertNamespaceSimple(string content, string rootNamespace)
{
const char eol = '\n';
if (string.IsNullOrWhiteSpace(rootNamespace))
{
return content
.Replace(RootNamespaceBeginTag + eol, "")
.Replace(RootNamespaceEndTag + eol, "");
}
var lines = content.Split(eol);
var startAt = ArrayUtility.IndexOf(lines, RootNamespaceBeginTag);
var endAt = ArrayUtility.IndexOf(lines, RootNamespaceEndTag);
lines[startAt] = $"namespace {rootNamespace}\n{{";
{
for (var i = startAt + 1; i < endAt; ++i)
{
lines[i] = $" {lines[i]}";
}
}
lines[endAt] = "}";
return string.Join(eol.ToString(), lines);
}
#endif
}
}

View File

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

View File

@ -0,0 +1,18 @@
{
"name": "VContainer.Editor",
"rootNamespace": "",
"references": [
"VContainer"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 7bce08a00b4d54cd694096abadbb897e
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: