diff --git a/src/DebugUtils/UnityDebugService.cs b/src/DebugUtils/UnityDebugService.cs index acca69e..fad1d26 100644 --- a/src/DebugUtils/UnityDebugService.cs +++ b/src/DebugUtils/UnityDebugService.cs @@ -11,6 +11,7 @@ namespace DCFApixels.DragonECS public class UnityDebugService : DebugService { private ProfilerMarker[] _profilerMarkers = new ProfilerMarker[64]; + static UnityDebugService() { Activate(); @@ -28,22 +29,30 @@ namespace DCFApixels.DragonECS return; } + string msg = AutoConvertObjectToString(v); bool hasTag = string.IsNullOrEmpty(tag) == false; if (hasTag) { - log = $"[{tag}] {v}"; string taglower = tag.ToLower(); - if (taglower.Contains("warning")) + switch (taglower) { - Debug.LogWarning(log); - return; + case "pass": + log = $"[{tag}] {msg}"; + Debug.Log(log); + break; + case "warning": + log = $"[{tag}] {msg}"; + Debug.LogWarning(log); + break; + case "error": + log = $"[{tag}] {msg}"; + Debug.LogError(log); + break; + default: + log = $"[{tag}] {msg}"; + Debug.Log(log); + break; } - if (taglower.Contains("error")) - { - Debug.LogError(log); - return; - } - Debug.Log(log); return; } Debug.Log(v); diff --git a/src/EcsPipelineTemplate/EcsPipelineTemplateSO.cs b/src/EcsPipelineTemplate/EcsPipelineTemplateSO.cs index b713dbc..e9e5091 100644 --- a/src/EcsPipelineTemplate/EcsPipelineTemplateSO.cs +++ b/src/EcsPipelineTemplate/EcsPipelineTemplateSO.cs @@ -19,15 +19,26 @@ namespace DCFApixels.DragonECS }; [SerializeField] + [ArrayElement] private string[] _layers = _defaultLayers.ToArray(); [SerializeField] - private Record[] _systems; + [ArrayElement] + private Record[] _records; + + public ReadOnlySpan Layers + { + get { return _layers; } + } + public ReadOnlySpan Records + { + get { return _records; } + } void IEcsModule.Import(EcsPipeline.Builder b) { b.Layers.MergeWith(_layers); - foreach (var s in _systems) + foreach (var s in _records) { if (s.target == null) { continue; } @@ -40,10 +51,10 @@ namespace DCFApixels.DragonECS EcsPipelineTemplate result = new EcsPipelineTemplate(); result.layers = new string[_layers.Length]; Array.Copy(_layers, result.layers, _layers.Length); - result.systems = new EcsPipelineTemplate.AddCommand[_systems.Length]; + result.systems = new EcsPipelineTemplate.AddCommand[_records.Length]; for (int i = 0; i < result.systems.Length; i++) { - ref var s = ref _systems[i]; + ref var s = ref _records[i]; result.systems[i] = new EcsPipelineTemplate.AddCommand(s.target, s.parameters); } return result; @@ -53,21 +64,23 @@ namespace DCFApixels.DragonECS { _layers = new string[template.layers.Length]; Array.Copy(template.layers, _layers, template.layers.Length); - _systems = new Record[template.systems.Length]; - for (int i = 0; i < _systems.Length; i++) + _records = new Record[template.systems.Length]; + for (int i = 0; i < _records.Length; i++) { ref var s = ref template.systems[i]; - _systems[i] = new Record(s.target, s.parameters); + _records[i] = new Record(s.target, s.parameters); } } - public void Validate() + public bool Validate() { - ValidateLayers(); - ValidateSystems(); + bool resutl = ValidateLayers(); + resutl |= ValidateSystems(); + return resutl; } - private void ValidateLayers() + private bool ValidateLayers() { + bool result = false; Dictionary builtinLayerIndexes = new Dictionary(); foreach (var item in _defaultLayers) { @@ -102,16 +115,21 @@ namespace DCFApixels.DragonECS int i = 0; foreach (var pair in builtinLayerIndexes.OrderBy(o => o.Value)) { - newLayers[pair.Value] = _defaultLayers[i]; + if (newLayers[pair.Value] != _defaultLayers[i]) + { + newLayers[pair.Value] = _defaultLayers[i]; + result = true; + } i++; } } _layers = newLayers.ToArray(); + return result; } - private void ValidateSystems() + private bool ValidateSystems() { - + return false; } [Serializable] @@ -128,4 +146,4 @@ namespace DCFApixels.DragonECS } } } -} +} \ No newline at end of file diff --git a/src/EcsPipelineTemplate/EcsPipelineTemplateSOEditor.cs b/src/EcsPipelineTemplate/EcsPipelineTemplateSOEditor.cs index 663a042..0fcc518 100644 --- a/src/EcsPipelineTemplate/EcsPipelineTemplateSOEditor.cs +++ b/src/EcsPipelineTemplate/EcsPipelineTemplateSOEditor.cs @@ -1,31 +1,220 @@ #if UNITY_EDITOR +using DCFApixels.DragonECS.Unity.Internal; +using System; using UnityEditor; +using UnityEditorInternal; using UnityEngine; namespace DCFApixels.DragonECS.Unity.Editors { - [CustomEditor(typeof(EcsPipelineTemplateSO))] - internal class EcsPipelineTemplateSOEditor : Editor + [CustomPropertyDrawer(typeof(EcsPipelineTemplateSO.Record))] + internal class EcsPipelineTemplateSORecordDrawer : ExtendedPropertyDrawer { - public override void OnInspectorGUI() + protected override void DrawCustom(Rect position, SerializedProperty property, GUIContent label) { - EditorGUI.BeginChangeCheck(); - base.OnInspectorGUI(); - if (EditorGUI.EndChangeCheck()) + if (IsArrayElement == false) { - Validate(); + Rect foldoutRect; + (foldoutRect, position) = position.VerticalSliceTop(OneLineHeight + Spacing); + property.isExpanded = EditorGUI.Foldout(foldoutRect, property.isExpanded, label); + if (property.isExpanded == false) + { + return; + } } - if (GUILayout.Button("Validate")) + + using (EcsGUI.SetIndentLevel(IsArrayElement ? EditorGUI.indentLevel : EditorGUI.indentLevel + 1)) { - Validate(); + Rect subPosition = position; + int depth = -1; + property.Next(true); + float height = 0f; + do + { + subPosition.y += height; + height = EditorGUI.GetPropertyHeight(property); + subPosition.height = height; + + EditorGUI.PropertyField(subPosition, property, true); + + } while (property.NextDepth(false, ref depth)); + } + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + float result = 0f; + if (IsArrayElement == false) + { + result += OneLineHeight; + if (property.isExpanded == false) + { + return result; + } + } + + + property.Next(true); + int depth = -1; + do + { + result += EditorGUI.GetPropertyHeight(property, true); + } while (property.NextDepth(false, ref depth)); + return result; + } + } + [CustomEditor(typeof(EcsPipelineTemplateSO))] + internal class EcsPipelineTemplateSOEditor : ExtendedEditor + { + private SerializedProperty _layersProp; + private SerializedProperty _recordsProp; + private ReorderableList _reorderableLayersList; + private ReorderableList _reorderableRecordsList; + + + protected override bool IsInit + { + get => _reorderableLayersList != null && _reorderableRecordsList != null; + } + + protected override void OnStaticInit() { } + protected override void OnInit() + { + _layersProp = FindProperty("_layers"); + _recordsProp = FindProperty("_records"); + + CreateLists(); + } + + private void CreateLists() + { + _reorderableLayersList = new ReorderableList(serializedObject, _layersProp, true, false, true, true); + _reorderableLayersList.onAddCallback += OnReorderableLayersListAdd; + _reorderableLayersList.onRemoveCallback += OnReorderableListRemove; + _reorderableLayersList.drawElementCallback += OnReorderableLayersListDrawElement; + _reorderableLayersList.onReorderCallback += OnReorderableListReorder; + + _reorderableRecordsList = new ReorderableList(serializedObject, _recordsProp, true, false, true, true); + _reorderableRecordsList.onAddCallback += OnReorderableRecordsListAdd; + _reorderableRecordsList.onRemoveCallback += OnReorderableListRemove; + _reorderableRecordsList.drawElementCallback += OnReorderableRecordsListDrawElement; + _reorderableRecordsList.elementHeightCallback += OnReorderableRecordsListElementHeight; + _reorderableRecordsList.onReorderCallback += OnReorderableListReorder; + } + + private void OnReorderableListReorder(ReorderableList list) + { + EcsGUI.Changed = true; + } + + #region _reorderableList + private void OnReorderableListRemove(ReorderableList list) + { + if (list.selectedIndices.Count <= 0) + { + if (list.serializedProperty.arraySize > 0) + { + list.serializedProperty.DeleteArrayElementAtIndex(list.serializedProperty.arraySize - 1); + } + return; + } + for (int i = list.selectedIndices.Count - 1; i >= 0; i--) + { + list.serializedProperty.DeleteArrayElementAtIndex(list.selectedIndices[i]); + } + EcsGUI.Changed = true; + } + #endregion + + #region _reorderableLayersList + private void OnReorderableLayersListDrawElement(Rect rect, int index, bool isActive, bool isFocused) + { + using (EcsGUI.CheckChanged()) + { + var elementProp = _layersProp.GetArrayElementAtIndex(index); + elementProp.stringValue = EditorGUI.TextField(rect, elementProp.stringValue); + } + } + private void OnReorderableLayersListAdd(ReorderableList list) + { + list.serializedProperty.InsertArrayElementAtIndex(list.serializedProperty.arraySize); + var added = list.serializedProperty.GetArrayElementAtIndex(list.serializedProperty.arraySize - 1); + added.stringValue = $"Layer-{DateTime.Now.Ticks}"; + added.serializedObject.ApplyModifiedProperties(); + EcsGUI.Changed = true; + } + #endregion + + #region _reorderableRecordsList + private void OnReorderableRecordsListDrawElement(Rect rect, int index, bool isActive, bool isFocused) + { + using (EcsGUI.CheckChanged()) + { + var prop = _recordsProp.GetArrayElementAtIndex(index); + var mrProp = prop.FindPropertyRelative(nameof(EcsPipelineTemplateSO.Record.target)); + ITypeMeta meta = mrProp.managedReferenceValue == null ? null : mrProp.managedReferenceValue.GetMeta(); + EcsGUI.DrawTypeMetaBlock(ref rect, prop, meta); + EditorGUI.PropertyField(rect, prop, true); + } + } + private float OnReorderableRecordsListElementHeight(int index) + { + return EcsGUI.GetTypeMetaBlockHeight(EditorGUI.GetPropertyHeight(_recordsProp.GetArrayElementAtIndex(index))); + } + + private void OnReorderableRecordsListAdd(ReorderableList list) + { + list.serializedProperty.arraySize += 1; + list.serializedProperty.GetArrayElementAtIndex(list.serializedProperty.arraySize - 1).ResetValues(); + EcsGUI.Changed = true; + } + + #endregion + + protected override void DrawCustom() + { + using (EcsGUI.CheckChanged()) + { + EditorGUI.BeginChangeCheck(); + + DrawLayoutNameList(_layersProp); + DrawRecordList(_recordsProp); + + EcsGUI.Changed = GUILayout.Button("Validate"); + if (EcsGUI.Changed) + { + serializedObject.ApplyModifiedProperties(); + Validate(); + Repaint(); + } } } private void Validate() { - foreach (var target in targets) + foreach (var target in Targets) { - ((EcsPipelineTemplateSO)target).Validate(); + target.Validate(); + EditorUtility.SetDirty(target); + } + serializedObject.Update(); + } + + private void DrawLayoutNameList(SerializedProperty layersProp) + { + using (EcsGUI.SetVerticalLayout()) + { + GUILayout.Label(UnityEditorUtility.GetLabel(layersProp.displayName), EditorStyles.boldLabel); + _reorderableLayersList.DoLayoutList(); + } + } + private void DrawRecordList(SerializedProperty recordsProp) + { + using (EcsGUI.SetVerticalLayout()) + { + GUILayout.Label(UnityEditorUtility.GetLabel(recordsProp.displayName), EditorStyles.boldLabel); + _reorderableRecordsList.DoLayoutList(); + //EditorGUILayout.PropertyField(recordsProp, UnityEditorUtility.GetLabel(recordsProp.displayName)); } } } diff --git a/src/EcsPipelineTemplate/ReferenceButtonAttribute.cs b/src/EcsPipelineTemplate/ReferenceButtonAttribute.cs index a8822cc..ef35bf7 100644 --- a/src/EcsPipelineTemplate/ReferenceButtonAttribute.cs +++ b/src/EcsPipelineTemplate/ReferenceButtonAttribute.cs @@ -247,6 +247,8 @@ namespace DCFApixels.DragonECS.Unity.Editors } currentProperty.serializedObject.ApplyModifiedProperties(); + + EcsGUI.Changed = true; } #endregion diff --git a/src/Editor/AddParamsDrawer.cs b/src/Editor/AddParamsDrawer.cs index 3239760..38eb2d6 100644 --- a/src/Editor/AddParamsDrawer.cs +++ b/src/Editor/AddParamsDrawer.cs @@ -1,89 +1,143 @@ #if UNITY_EDITOR using DCFApixels.DragonECS.Unity.Internal; +using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace DCFApixels.DragonECS.Unity.Editors { [CustomPropertyDrawer(typeof(AddParams))] - internal class AddParamsDrawer : PropertyDrawer + internal class AddParamsDrawer : ExtendedPropertyDrawer { - private float SingleLineHeight => EditorGUIUtility.singleLineHeight; - private float Spacing => EditorGUIUtility.standardVerticalSpacing; + private static Dictionary _labelCache = new Dictionary() + { + { 0, "" }, + { 1, "Layer" }, + { 2, "Order" }, + { 3, "Layer, Order" }, + { 4, "IsUnique" }, + { 5, "Layer, IsUnique" }, + { 6, "Order, IsUnique" }, + { 7, "Layer, Order, IsUnique" }, + { (byte)AddParamsFlags.NoImport | 0, "NoImport" }, + { (byte)AddParamsFlags.NoImport | 1, "NoImport, Layer" }, + { (byte)AddParamsFlags.NoImport | 2, "NoImport, Order" }, + { (byte)AddParamsFlags.NoImport | 3, "NoImport, Layer, Order" }, + { (byte)AddParamsFlags.NoImport | 4, "NoImport, IsUnique" }, + { (byte)AddParamsFlags.NoImport | 5, "NoImport, Layer, IsUnique" }, + { (byte)AddParamsFlags.NoImport | 6, "NoImport, Order, IsUnique" }, + { (byte)AddParamsFlags.NoImport | 7, "NoImport, Layer, Order, IsUnique" }, + { byte.MaxValue, "NoImport, Layer, Order, IsUnique" }, + }; + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { - return !property.isExpanded ? EditorGUIUtility.singleLineHeight + Spacing : EditorGUIUtility.singleLineHeight * 4f + Spacing * 3f; + //return !property.isExpanded ? + // EditorGUIUtility.singleLineHeight + Spacing : + // EditorGUIUtility.singleLineHeight * 5f + Spacing * 4f; + return EditorGUI.GetPropertyHeight(property); } - public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + protected override void DrawCustom(Rect position, SerializedProperty property, GUIContent label) { + Event e = Event.current; + var flagsProp = property.FindPropertyRelative("flags"); AddParamsFlags flags = (AddParamsFlags)flagsProp.enumValueFlag; - var (foldoutRect, contentRect) = position.VerticalSliceTop(SingleLineHeight + Spacing); + var (foldoutRect, contentRect) = position.VerticalSliceTop(OneLineHeight + Spacing); + var (fieldsRects, checkboxRects) = contentRect.HorizontalSliceRight(OneLineHeight); - var (fieldsRects, checkboxRects) = contentRect.HorizontalSliceRight(SingleLineHeight); + Rect labelField; + (foldoutRect, labelField) = foldoutRect.HorizontalSliceLeft(EditorGUIUtility.labelWidth); - property.isExpanded = EditorGUI.Foldout(foldoutRect, property.isExpanded, label); - //property.isExpanded = true; - using (EcsGUI.UpIndentLevel()) + using (EcsGUI.CheckChanged()) { + property.isExpanded = EditorGUI.Foldout(foldoutRect, property.isExpanded, label); - EditorGUI.BeginChangeCheck(); - - checkboxRects = checkboxRects.Move(EditorGUIUtility.standardVerticalSpacing, 0); - - Rect checkboxRect = checkboxRects; - checkboxRect.height = SingleLineHeight; - Rect fieldRect = fieldsRects; - fieldRect.height = SingleLineHeight; - - //LayerName - property.Next(true); - using (EcsGUI.SetIndentLevel(0)) + using (EcsGUI.SetAlignment(EditorStyles.miniLabel, TextAnchor.MiddleRight)) { - flags = flags.SetOverwriteLayerName(EditorGUI.Toggle(checkboxRect, flags.IsOverwriteLayerName())); + if (_labelCache.TryGetValue((byte)(flags.Normalize()), out string str) == false) + { + str = _labelCache[0]; + } + GUI.Label(labelField, str, EditorStyles.miniLabel); } - using (EcsGUI.SetEnable(flags.IsOverwriteLayerName())) + + if (property.isExpanded == false) { + goto exti; + } + + //property.isExpanded = true; + using (EcsGUI.UpIndentLevel()) + { + checkboxRects = checkboxRects.Move(EditorGUIUtility.standardVerticalSpacing, 0); + + Rect checkboxRect = checkboxRects; + checkboxRect.height = OneLineHeight; + Rect fieldRect = fieldsRects; + fieldRect.height = OneLineHeight; + + //LayerName + property.Next(true); + using (EcsGUI.SetIndentLevel(0)) + { + flags = flags.SetOverwriteLayerName(EditorGUI.Toggle(checkboxRect, flags.IsOverwriteLayerName())); + } + using (EcsGUI.SetEnable(flags.IsOverwriteLayerName())) + { + EditorGUI.PropertyField(fieldRect, property, UnityEditorUtility.GetLabel(property.displayName), true); + } + + checkboxRect = checkboxRect.Move(0, OneLineHeight + Spacing); + fieldRect = fieldRect.Move(0, OneLineHeight + Spacing); + + //SortOrder + property.Next(false); + using (EcsGUI.SetIndentLevel(0)) + { + flags = flags.SetOverwriteSortOrder(EditorGUI.Toggle(checkboxRect, flags.IsOverwriteSortOrder())); + } + using (EcsGUI.SetEnable(flags.IsOverwriteSortOrder())) + { + EditorGUI.PropertyField(fieldRect, property, UnityEditorUtility.GetLabel(property.displayName), true); + } + + checkboxRect = checkboxRect.Move(0, OneLineHeight + Spacing); + fieldRect = fieldRect.Move(0, OneLineHeight + Spacing); + + //IsUnique + property.Next(false); + using (EcsGUI.SetIndentLevel(0)) + { + flags = flags.SetOverwriteIsUnique(EditorGUI.Toggle(checkboxRect, flags.IsOverwriteIsUnique())); + } + using (EcsGUI.SetEnable(flags.IsOverwriteIsUnique())) + { + EditorGUI.PropertyField(fieldRect, property, UnityEditorUtility.GetLabel(property.displayName), true); + } + + if (EcsGUI.Changed) + { + flagsProp.enumValueFlag = (int)flags; + } + + checkboxRect = checkboxRect.Move(0, OneLineHeight + Spacing); + fieldRect = fieldRect.Move(0, OneLineHeight + Spacing); + fieldRect.xMax += OneLineHeight + Spacing; + property.Next(false); EditorGUI.PropertyField(fieldRect, property, UnityEditorUtility.GetLabel(property.displayName), true); } - checkboxRect = checkboxRect.Move(0, SingleLineHeight + Spacing); - fieldRect = fieldRect.Move(0, SingleLineHeight + Spacing); + exti:; - //SortOrder - property.Next(false); - using (EcsGUI.SetIndentLevel(0)) + //EcsGUI.Changed = Event.current.type == EventType.MouseUp; + if (EcsGUI.Changed) { - flags = flags.SetOverwriteSortOrder(EditorGUI.Toggle(checkboxRect, flags.IsOverwriteSortOrder())); - } - using (EcsGUI.SetEnable(flags.IsOverwriteSortOrder())) - { - EditorGUI.PropertyField(fieldRect, property, UnityEditorUtility.GetLabel(property.displayName), true); - } - - checkboxRect = checkboxRect.Move(0, SingleLineHeight + Spacing); - fieldRect = fieldRect.Move(0, SingleLineHeight + Spacing); - - //IsUnique - property.Next(false); - using (EcsGUI.SetIndentLevel(0)) - { - flags = flags.SetOverwriteIsUnique(EditorGUI.Toggle(checkboxRect, flags.IsOverwriteIsUnique())); - } - using (EcsGUI.SetEnable(flags.IsOverwriteIsUnique())) - { - EditorGUI.PropertyField(fieldRect, property, UnityEditorUtility.GetLabel(property.displayName), true); - } - - if (EditorGUI.EndChangeCheck()) - { - flagsProp.enumValueFlag = (int)flags; property.serializedObject.ApplyModifiedProperties(); } - } } } diff --git a/src/EntityTemplate/Templates/ComponentTemplateBase.cs b/src/EntityTemplate/Templates/ComponentTemplateBase.cs index 4b03d40..76f92c9 100644 --- a/src/EntityTemplate/Templates/ComponentTemplateBase.cs +++ b/src/EntityTemplate/Templates/ComponentTemplateBase.cs @@ -35,6 +35,7 @@ namespace DCFApixels.DragonECS { #region Properties public abstract Type Type { get; } + public virtual ITypeMeta BaseMeta { get { return null; } } public virtual string Name { get { return string.Empty; } } public virtual MetaColor Color { get { return new MetaColor(MetaColor.Black); } } public virtual MetaGroup Group { get { return MetaGroup.Empty; } } @@ -63,6 +64,7 @@ namespace DCFApixels.DragonECS private byte _offset; // Fucking Unity drove me crazy with the error "Cannot get managed reference index with out bounds offset". This workaround helps avoid that error. #region Properties + public sealed override ITypeMeta BaseMeta { get { return Meta; } } public sealed override Type Type { get { return typeof(T); } } public override string Name { get { return Meta.Name; } } public override MetaColor Color { get { return Meta.Color; } } diff --git a/src/Internal/Editor/EcsGUI.cs b/src/Internal/Editor/EcsGUI.cs index 8320956..a86b560 100644 --- a/src/Internal/Editor/EcsGUI.cs +++ b/src/Internal/Editor/EcsGUI.cs @@ -12,10 +12,65 @@ namespace DCFApixels.DragonECS.Unity.Editors internal static class EcsGUI { #region Scores - public struct LabelWidthScore : IDisposable + private static int _changedCounter = 0; + private static bool _changed = false; + + public static int ChangedCounter => _changedCounter; + public static bool Changed + { + get + { + _changed = _changed || GUI.changed; + return _changed; + } + set + { + _changed = _changed || GUI.changed || value; + GUI.changed = _changed; + } + } + public readonly struct CheckChangedScope : IDisposable + { + private readonly bool _value; + public CheckChangedScope(bool value) + { + _value = value; + _changedCounter++; + _changed = false; + } + public static CheckChangedScope New() { return new CheckChangedScope(Changed); } + public void Dispose() + { + Changed = Changed || _value; + _changedCounter--; + //if(_changedCounter <= 0 && Event.current.type == EventType.Repaint) + if (_changedCounter <= 0) + { + _changedCounter = 0; + _changed = false; + } + } + } + + public struct VerticalLayoutScope : IDisposable + { + public static VerticalLayoutScope New() { GUILayout.BeginVertical(); return new VerticalLayoutScope(); } + public void Dispose() { GUILayout.EndVertical(); } + } + public struct HorizontalLayoutScope : IDisposable + { + public static HorizontalLayoutScope New() { GUILayout.BeginVertical(); return new HorizontalLayoutScope(); } + public void Dispose() { GUILayout.EndVertical(); } + } + public struct ScrollViewLayoutScope : IDisposable + { + public ScrollViewLayoutScope(ref Vector2 pos) { pos = GUILayout.BeginScrollView(pos); } + public void Dispose() { GUILayout.EndScrollView(); } + } + public readonly struct LabelWidthScope : IDisposable { private readonly float _value; - public LabelWidthScore(float value) + public LabelWidthScope(float value) { _value = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = value; @@ -116,13 +171,18 @@ namespace DCFApixels.DragonECS.Unity.Editors } public void Dispose() { Target.fontStyle = _value; } } - #endregion + public static CheckChangedScope CheckChanged() => CheckChangedScope.New(); + public static ScrollViewLayoutScope SetScrollViewLayout(ref Vector2 pos) => new ScrollViewLayoutScope(ref pos); + public static HorizontalLayoutScope SetHorizontalLayout() => HorizontalLayoutScope.New(); + public static VerticalLayoutScope SetVerticalLayout() => VerticalLayoutScope.New(); public static FontStyleScope SetFontStyle(GUIStyle target, FontStyle value) => new FontStyleScope(target, value); + public static FontStyleScope SetFontStyle(FontStyle value) => new FontStyleScope(GUI.skin.label, value); public static FontStyleScope SetFontStyle(GUIStyle target) => new FontStyleScope(target); public static FontSizeScope SetFontSize(GUIStyle target, int value) => new FontSizeScope(target, value); public static FontSizeScope SetFontSize(GUIStyle target) => new FontSizeScope(target); public static AlignmentScope SetAlignment(GUIStyle target, TextAnchor value) => new AlignmentScope(target, value); + public static AlignmentScope SetAlignment(TextAnchor value) => new AlignmentScope(GUI.skin.label, value); public static AlignmentScope SetAlignment(GUIStyle target) => new AlignmentScope(target); public static IndentLevelScope SetIndentLevel(int level) => new IndentLevelScope(level); public static IndentLevelScope UpIndentLevel() => new IndentLevelScope(EditorGUI.indentLevel + 1); @@ -136,8 +196,7 @@ namespace DCFApixels.DragonECS.Unity.Editors 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 private static readonly BindingFlags fieldFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; @@ -320,7 +379,7 @@ namespace DCFApixels.DragonECS.Unity.Editors } public static void EntityBar(Rect position, bool isPlaceholder, EntityStatus status, int id = 0, short gen = 0, short world = 0) { - using (new LabelWidthScore(0f)) + using (new LabelWidthScope(0f)) { var (entityInfoRect, statusRect) = RectUtility.VerticalSliceBottom(position, 3f); @@ -346,7 +405,7 @@ namespace DCFApixels.DragonECS.Unity.Editors } private static void EntityBar_Internal(Rect position, bool isPlaceHolder, int id = 0, short gen = 0, short world = 0) { - using (new LabelWidthScore(0f)) + using (new LabelWidthScope(0f)) { Color w = Color.gray; w.a = 0.6f; @@ -385,13 +444,122 @@ namespace DCFApixels.DragonECS.Unity.Editors } #endregion - internal static int GetChildPropertiesCount(SerializedProperty property, Type type, out bool isEmpty) + #region DrawTypeMetaBlock + private static float DrawTypeMetaBlockPadding => EditorGUIUtility.standardVerticalSpacing; + private static float SingleLineWithPadding => EditorGUIUtility.singleLineHeight + DrawTypeMetaBlockPadding * 4f; + public static float GetTypeMetaBlockHeight(float contentHeight) + { + return DrawTypeMetaBlockPadding * 2 + contentHeight; + } + public static void DrawTypeMetaBlock(ref Rect position, SerializedProperty property, ITypeMeta meta) + { + if (meta == null) + { + return; + } + GUIContent label = UnityEditorUtility.GetLabel(property.displayName); + + + var counter = property.Copy(); + int positionCountr = int.MaxValue; + int depth = -1; + while (counter.NextVisibleDepth(false, ref depth)) + { + positionCountr--; + } + + //var counter = property.Copy(); + //int positionCountr = int.MaxValue; + //while (counter.NextVisible(false)) + //{ + // positionCountr--; + //} + + string name = meta.Name; + string description = meta.Description.Text; + + Color panelColor = EcsGUI.SelectPanelColor(meta, positionCountr, -1).Desaturate(EscEditorConsts.COMPONENT_DRAWER_DESATURATE); + + Color alphaPanelColor = panelColor; + alphaPanelColor.a = EscEditorConsts.COMPONENT_DRAWER_ALPHA; + + + EditorGUI.BeginChangeCheck(); + EditorGUI.DrawRect(position, alphaPanelColor); + + Rect paddingPosition = RectUtility.AddPadding(position, DrawTypeMetaBlockPadding * 2f); + + Rect optionButton = position; + optionButton.center -= new Vector2(0, optionButton.height); + optionButton.yMin = optionButton.yMax; + optionButton.yMax += HeadIconsRect.height; + optionButton.xMin = optionButton.xMax - 64; + optionButton.center += Vector2.up * DrawTypeMetaBlockPadding * 1f; + //Canceling isExpanded + if (HitTest(optionButton) && Event.current.type == EventType.MouseUp) + { + property.isExpanded = !property.isExpanded; + } + + //Close button + optionButton.xMin = optionButton.xMax - HeadIconsRect.width; + if (CloseButton(optionButton)) + { + property.ResetValues(); + return; + } + //Edit script button + if (UnityEditorUtility.TryGetScriptAsset(meta.Type, out MonoScript script)) + { + optionButton = HeadIconsRect.MoveTo(optionButton.center - (Vector2.right * optionButton.width)); + ScriptAssetButton(optionButton, script); + } + //Description icon + if (string.IsNullOrEmpty(description) == false) + { + optionButton = HeadIconsRect.MoveTo(optionButton.center - (Vector2.right * optionButton.width)); + DescriptionIcon(optionButton, description); + } + + //if (string.IsNullOrEmpty(label.text)) + //{ + // EditorGUI.indentLevel++; + // EditorGUI.PrefixLabel(position.AddPadding(0, 0, 0, position.height - SingleLineWithPadding), UnityEditorUtility.GetLabel(name)); + // EditorGUI.indentLevel--; + //} + //else + //{ + // GUI.Label(position.AddPadding(EditorGUIUtility.labelWidth, 0, 0, position.height - SingleLineWithPadding), name); + //} + + position = paddingPosition; + } + #endregion + + #region NextDepth/GetChildPropertiesCount + internal static bool NextVisibleDepth(this SerializedProperty property, bool child, ref int depth) + { + if (depth < 0) + { + depth = property.depth; + } + return property.NextVisible(child) && property.depth >= depth; + } + internal static bool NextDepth(this SerializedProperty property, bool child, ref int depth) + { + if (depth < 0) + { + depth = property.depth; + } + return property.Next(child) && property.depth >= depth; + } + internal static int GetChildPropertiesCount(this SerializedProperty property, Type type, out bool isEmpty) { int result = GetChildPropertiesCount(property); isEmpty = result <= 0 && type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Length <= 0; return result; } - internal static int GetChildPropertiesCount(SerializedProperty property) + internal static int GetChildPropertiesCount(this SerializedProperty property) { var propsCounter = property.Copy(); int lastDepth = propsCounter.depth; @@ -404,6 +572,9 @@ namespace DCFApixels.DragonECS.Unity.Editors } return result; } + #endregion + + #region SelectPanelColor public static Color SelectPanelColor(ITypeMeta meta, int index, int total) { var trueMeta = meta.Type.ToMeta(); @@ -431,6 +602,9 @@ namespace DCFApixels.DragonECS.Unity.Editors } } } + #endregion + + #region Other Elements public static bool AddComponentButton(Rect position, out Rect dropDownRect) { dropDownRect = RectUtility.AddPadding(position, 20f, 20f, 12f, 2f); @@ -471,7 +645,11 @@ namespace DCFApixels.DragonECS.Unity.Editors EditorGUI.BeginProperty(position, label, property); EditorGUI.EndProperty(); } + #endregion + #region SerializeReferenceFixer + + #endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/Internal/Editor/ExtendedEditor.cs b/src/Internal/Editor/ExtendedEditor.cs new file mode 100644 index 0000000..1a5e434 --- /dev/null +++ b/src/Internal/Editor/ExtendedEditor.cs @@ -0,0 +1,176 @@ +namespace DCFApixels.DragonECS.Unity.Internal +{ + using System; + + [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = true)] + internal sealed class ArrayElementAttribute : Attribute { } +} + +#if UNITY_EDITOR +namespace DCFApixels.DragonECS.Unity.Editors +{ + using DCFApixels.DragonECS.Unity.Internal; + using global::Unity.Collections.LowLevel.Unsafe; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using UnityEditor; + using UnityEngine; + using UnityObject = UnityEngine.Object; + + + internal abstract class ExtendedEditor : Editor + { + private bool _isStaticInit = false; + private bool _isInit = false; + + protected float SingleLineHeight + { + get => EditorGUIUtility.singleLineHeight; + } + protected float Spacing + { + get => EditorGUIUtility.standardVerticalSpacing; + } + + protected virtual bool IsStaticInit { get { return _isStaticInit; } } + protected virtual bool IsInit { get { return _isInit; } } + protected void StaticInit() + { + if (IsStaticInit) { return; } + _isStaticInit = true; + OnStaticInit(); + } + protected void Init() + { + if (IsInit) { return; } + _isInit = true; + OnInit(); + } + protected virtual void OnStaticInit() { } + protected virtual void OnInit() { } + + public sealed override void OnInspectorGUI() + { + using (EcsGUI.CheckChanged()) + { + StaticInit(); + Init(); + DrawCustom(); + } + } + + protected abstract void DrawCustom(); + protected void DrawDefault() + { + base.OnInspectorGUI(); + } + + + protected SerializedProperty FindProperty(string name) + { + return serializedObject.FindProperty(name); + } + } + internal abstract class ExtendedEditor : ExtendedEditor where T : UnityObject + { + + public T Target + { + get + { + var obj = target; + return UnsafeUtility.As(ref obj); + } + } + public T[] Targets + { + get + { + var obj = targets; + return UnsafeUtility.As(ref obj); + } + } + } + internal abstract class ExtendedPropertyDrawer : PropertyDrawer + { + private bool _isStaticInit = false; + private bool _isInit = false; + + private IEnumerable _attributes = null; + + private bool? _isArrayElement = null; + protected bool IsArrayElement + { + get + { + if (_isArrayElement == null) + { + _isArrayElement = Attributes.Any(o => o is ArrayElementAttribute); + } + return _isArrayElement.Value; + } + } + + private IEnumerable Attributes + { + get + { + if (_attributes == null) + { + _attributes = fieldInfo.GetCustomAttributes(); + } + return _attributes; + } + } + protected float OneLineHeight + { + get => EditorGUIUtility.singleLineHeight; + } + protected float Spacing + { + get => EditorGUIUtility.standardVerticalSpacing; + } + + protected virtual bool IsStaticInit { get { return _isStaticInit; } } + protected virtual bool IsInit { get { return _isInit; } } + protected void StaticInit() + { + if (IsStaticInit) { return; } + _isStaticInit = true; + OnStaticInit(); + } + protected void Init() + { + if (IsInit) { return; } + _isInit = true; + OnInit(); + } + protected virtual void OnStaticInit() { } + protected virtual void OnInit() { } + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + using (EcsGUI.CheckChanged()) + { + StaticInit(); + Init(); + DrawCustom(position, property, label); + } + } + protected abstract void DrawCustom(Rect position, SerializedProperty property, GUIContent label); + } + internal abstract class ExtendedPropertyDrawer : ExtendedPropertyDrawer + { + protected TAttribute Attribute + { + get + { + var obj = attribute; + return UnsafeUtility.As(ref obj); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/Internal/Editor/ExtendedEditor.cs.meta b/src/Internal/Editor/ExtendedEditor.cs.meta new file mode 100644 index 0000000..bd24c24 --- /dev/null +++ b/src/Internal/Editor/ExtendedEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e9a6ec3374a23a441bc22494aca34430 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Internal/Editor/UnityEditorUtility.cs b/src/Internal/Editor/UnityEditorUtility.cs index d4f916b..0a5c91f 100644 --- a/src/Internal/Editor/UnityEditorUtility.cs +++ b/src/Internal/Editor/UnityEditorUtility.cs @@ -116,6 +116,115 @@ namespace DCFApixels.DragonECS.Unity.Editors private static Dictionary scriptsAssets = new Dictionary(256); + internal static void ResetValues(this SerializedProperty property, bool isExpand = false) + { + property.isExpanded = isExpand; + switch (property.propertyType) + { + case SerializedPropertyType.Generic: + try + //TODO хз как с этим работать, но это говно постоянно кидает + //InvalidOperationException: The operation is not possible when moved past all properties (Next returned false) + //и не дает инструментов и шансов этого избежать + { + while (property.Next(true)) + { + property.ResetValues(isExpand); + } + } + catch (Exception) { } + break; + case SerializedPropertyType.Integer: + property.intValue = default; + break; + case SerializedPropertyType.Boolean: + property.boolValue = default; + break; + case SerializedPropertyType.Float: + property.floatValue = default; + break; + case SerializedPropertyType.String: + property.stringValue = string.Empty; + break; + case SerializedPropertyType.Color: + property.colorValue = default; + break; + case SerializedPropertyType.ObjectReference: + property.objectReferenceValue = null; + break; + case SerializedPropertyType.LayerMask: + property.intValue = default; + break; + case SerializedPropertyType.Enum: + property.enumValueIndex = default; + break; + case SerializedPropertyType.Vector2: + property.vector2Value = default; + break; + case SerializedPropertyType.Vector3: + property.vector3Value = default; + break; + case SerializedPropertyType.Vector4: + property.vector4Value = default; + break; + case SerializedPropertyType.Rect: + property.rectValue = default; + break; + case SerializedPropertyType.ArraySize: + property.ClearArray(); + break; + case SerializedPropertyType.Character: + property.intValue = default; + break; + case SerializedPropertyType.AnimationCurve: + property.animationCurveValue = new AnimationCurve(); + break; + case SerializedPropertyType.Bounds: + property.boundsValue = default; + break; + case SerializedPropertyType.Gradient: +#if UNITY_2022_1_OR_NEWER + property.gradientValue = new Gradient();; + +#else + Debug.LogWarning($"Unsupported SerializedPropertyType: {property.propertyType}"); +#endif + break; + case SerializedPropertyType.Quaternion: + property.quaternionValue = Quaternion.identity; + break; + case SerializedPropertyType.ExposedReference: + property.objectReferenceValue = null; + break; + case SerializedPropertyType.FixedBufferSize: + for (int i = 0, iMax = property.fixedBufferSize; i < iMax; i++) + { + property.GetFixedBufferElementAtIndex(i).intValue = default; + } + break; + case SerializedPropertyType.Vector2Int: + property.vector2IntValue = default; + break; + case SerializedPropertyType.Vector3Int: + property.vector3IntValue = default; + break; + case SerializedPropertyType.RectInt: + property.rectIntValue = default; + break; + case SerializedPropertyType.BoundsInt: + property.boundsIntValue = default; + break; + case SerializedPropertyType.ManagedReference: + property.managedReferenceValue = default; + break; + case SerializedPropertyType.Hash128: + property.hash128Value = default; + break; + default: + Debug.LogWarning($"Unsupported SerializedPropertyType: {property.propertyType}"); + break; + } + } internal static bool TryGetScriptAsset(Type type, out MonoScript script) { if (scriptsAssets.TryGetValue(type, out script) == false)