390 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
			
		
		
	
	
			390 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
| using System.ComponentModel;
 | |
| 
 | |
| namespace SRDebugger.Editor
 | |
| {
 | |
|     using System;
 | |
|     using System.Collections.Generic;
 | |
|     using System.Linq;
 | |
|     using Internal;
 | |
|     using SRF;
 | |
|     using UI.Controls.Data;
 | |
|     using UnityEngine;
 | |
|     using UnityEditor;
 | |
| 
 | |
|     public class SROptionsWindow : EditorWindow
 | |
|     {
 | |
|         [MenuItem(SRDebugPaths.SROptionsMenuItemPath)]
 | |
|         public static void Open()
 | |
|         {
 | |
|             var window = GetWindow<SROptionsWindow>(false, "SROptions", true);
 | |
|             window.minSize = new Vector2(100, 100);
 | |
|             window.Show();
 | |
|         }
 | |
| 
 | |
|         [Serializable]
 | |
|         private class CategoryState
 | |
|         {
 | |
|             public string Name;
 | |
|             public bool IsOpen;
 | |
|         }
 | |
| 
 | |
|         [SerializeField]
 | |
|         private List<CategoryState> _categoryStates = new List<CategoryState>();
 | |
| 
 | |
|         private Dictionary<Type, Action<OptionDefinition>> _typeLookup;
 | |
| 
 | |
|         private Dictionary<string, List<OptionDefinition>> _options;
 | |
| 
 | |
|         private Vector2 _scrollPosition;
 | |
|         private bool _queueRefresh;
 | |
| 
 | |
|         [NonSerialized] private GUIStyle _divider;
 | |
|         [NonSerialized] private GUIStyle _foldout;
 | |
| 
 | |
|         public void OnInspectorUpdate()
 | |
|         {
 | |
|             if (EditorApplication.isPlaying && _options == null)
 | |
|             {
 | |
|                 Populate();
 | |
|                 _queueRefresh = true;
 | |
|             }
 | |
|             else if (!EditorApplication.isPlaying && _options != null)
 | |
|             {
 | |
|                 _options = null;
 | |
|                 _queueRefresh = true;
 | |
|             }
 | |
| 
 | |
|             if (_queueRefresh)
 | |
|             {
 | |
|                 Repaint();
 | |
|             }
 | |
| 
 | |
|             _queueRefresh = false;
 | |
|         }
 | |
| 
 | |
|         void PopulateTypeLookup()
 | |
|         {
 | |
|             _typeLookup = new Dictionary<Type, Action<OptionDefinition>>()
 | |
|             {
 | |
|                 {typeof(int), OnGUI_Int},
 | |
|                 {typeof(float), OnGUI_Float},
 | |
|                 {typeof(double), OnGUI_Double},
 | |
|                 {typeof(string), OnGUI_String},
 | |
|                 {typeof(bool), OnGUI_Boolean },
 | |
|                 {typeof(uint), OnGUI_AnyInteger},
 | |
|                 {typeof(ushort), OnGUI_AnyInteger},
 | |
|                 {typeof(short), OnGUI_AnyInteger},
 | |
|                 {typeof(sbyte), OnGUI_AnyInteger},
 | |
|                 {typeof(byte), OnGUI_AnyInteger},
 | |
|                 {typeof(long), OnGUI_AnyInteger},
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         void Populate()
 | |
|         {
 | |
|             if (_typeLookup == null)
 | |
|             {
 | |
|                 PopulateTypeLookup();
 | |
|             }
 | |
| 
 | |
|             _options = new Dictionary<string, List<OptionDefinition>>();
 | |
|             
 | |
|             foreach (var option in Service.Options.Options)
 | |
|             {
 | |
|                 List<OptionDefinition> list;
 | |
| 
 | |
|                 if (!_options.TryGetValue(option.Category, out list))
 | |
|                 {
 | |
|                     list = new List<OptionDefinition>();
 | |
|                     _options[option.Category] = list;
 | |
|                 }
 | |
| 
 | |
|                 list.Add(option);
 | |
|             }
 | |
| 
 | |
|             foreach (var kv in _options)
 | |
|             {
 | |
|                 kv.Value.Sort((d1, d2) => d1.SortPriority.CompareTo(d2.SortPriority));
 | |
|             }
 | |
| 
 | |
|             Service.Options.OptionsValueUpdated += OptionsOnOptionsValueUpdated;
 | |
|         }
 | |
| 
 | |
|         private void OptionsOnOptionsValueUpdated(object sender, PropertyChangedEventArgs e)
 | |
|         {
 | |
|             _queueRefresh = true;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         void OnGUI()
 | |
|         {
 | |
|             EditorGUILayout.Space();
 | |
| 
 | |
|             if (!EditorApplication.isPlayingOrWillChangePlaymode || _options == null)
 | |
|             {
 | |
|                 EditorGUILayout.BeginHorizontal();
 | |
|                 GUILayout.FlexibleSpace();
 | |
|                 GUILayout.Label("SROptions can only be edited in play-mode.");
 | |
|                 GUILayout.FlexibleSpace();
 | |
|                 EditorGUILayout.EndHorizontal();
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             if (_divider == null)
 | |
|             {
 | |
|                 _divider = new GUIStyle(GUI.skin.box);
 | |
|                 _divider.stretchWidth = true;
 | |
|                 _divider.fixedHeight = 2;
 | |
|             }
 | |
| 
 | |
|             if (_foldout == null)
 | |
|             {
 | |
|                 _foldout = new GUIStyle(EditorStyles.foldout);
 | |
|                 _foldout.fontStyle = FontStyle.Bold;
 | |
|             }
 | |
| 
 | |
|             _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
 | |
| 
 | |
|             foreach (var kv in _options)
 | |
|             {
 | |
|                 var state = _categoryStates.FirstOrDefault(p => p.Name == kv.Key);
 | |
| 
 | |
|                 if (state == null)
 | |
|                 {
 | |
|                     state = new CategoryState()
 | |
|                     {
 | |
|                         Name = kv.Key,
 | |
|                         IsOpen = true
 | |
|                     };
 | |
|                     _categoryStates.Add(state);
 | |
|                 }
 | |
|                 
 | |
|                 state.IsOpen = EditorGUILayout.Foldout(state.IsOpen, kv.Key, _foldout);
 | |
| 
 | |
|                 if (!state.IsOpen)
 | |
|                     continue;
 | |
| 
 | |
|                 EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
 | |
|                 OnGUI_Category(kv.Value);
 | |
|                 EditorGUILayout.Space();
 | |
|                 EditorGUILayout.EndHorizontal();
 | |
|             }
 | |
| 
 | |
|             EditorGUILayout.EndScrollView();
 | |
|         }
 | |
| 
 | |
|         void OnGUI_Category(List<OptionDefinition> options)
 | |
|         {
 | |
|             for (var i = 0; i < options.Count; i++)
 | |
|             {
 | |
|                 var op = options[i];
 | |
| 
 | |
|                 if (op.Property != null)
 | |
|                 {
 | |
|                     OnGUI_Property(op);
 | |
|                 } else if (op.Method != null)
 | |
|                 {
 | |
|                     OnGUI_Method(op);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void OnGUI_Method(OptionDefinition op)
 | |
|         {
 | |
|             if (GUILayout.Button(op.Name))
 | |
|             {
 | |
|                 op.Method.Invoke(null);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void OnGUI_Property(OptionDefinition op)
 | |
|         {
 | |
|             Action<OptionDefinition> method;
 | |
| 
 | |
|             if (op.Property.PropertyType.IsEnum)
 | |
|             {
 | |
|                 method = OnGUI_Enum;
 | |
|             }
 | |
|             else if (!_typeLookup.TryGetValue(op.Property.PropertyType, out method))
 | |
|             {
 | |
|                 OnGUI_Unsupported(op);
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             if (!op.Property.CanWrite)
 | |
|             {
 | |
|                 EditorGUI.BeginDisabledGroup(true);
 | |
|             }
 | |
| 
 | |
|             method(op);
 | |
| 
 | |
|             if (!op.Property.CanWrite)
 | |
|             {
 | |
|                 EditorGUI.EndDisabledGroup();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void OnGUI_String(OptionDefinition op)
 | |
|         {
 | |
|             EditorGUI.BeginChangeCheck();
 | |
|             var newValue = EditorGUILayout.TextField(op.Name, (string) op.Property.GetValue());
 | |
| 
 | |
|             if (EditorGUI.EndChangeCheck())
 | |
|             {
 | |
|                 op.Property.SetValue(Convert.ChangeType(newValue, op.Property.PropertyType));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void OnGUI_Boolean(OptionDefinition op)
 | |
|         {
 | |
|             EditorGUI.BeginChangeCheck();
 | |
|             var newValue = EditorGUILayout.Toggle(op.Name, (bool) op.Property.GetValue());
 | |
| 
 | |
|             if (EditorGUI.EndChangeCheck())
 | |
|             {
 | |
|                 op.Property.SetValue(Convert.ChangeType(newValue, op.Property.PropertyType));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void OnGUI_Enum(OptionDefinition op)
 | |
|         {
 | |
|             EditorGUI.BeginChangeCheck();
 | |
|             var newValue = EditorGUILayout.EnumPopup(op.Name, (Enum)op.Property.GetValue());
 | |
| 
 | |
|             if (EditorGUI.EndChangeCheck())
 | |
|             {
 | |
|                 op.Property.SetValue(Convert.ChangeType(newValue, op.Property.PropertyType));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void OnGUI_Int(OptionDefinition op)
 | |
|         {
 | |
|             var range = op.Property.GetAttribute<NumberRangeAttribute>();
 | |
| 
 | |
|             int newValue;
 | |
| 
 | |
|             EditorGUI.BeginChangeCheck();
 | |
| 
 | |
|             if (range != null)
 | |
|             {
 | |
|                 newValue = EditorGUILayout.IntSlider(op.Name, (int)op.Property.GetValue(), (int)range.Min, (int)range.Max);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 newValue = EditorGUILayout.IntField(op.Name, (int) op.Property.GetValue());
 | |
|             }
 | |
| 
 | |
|             if (EditorGUI.EndChangeCheck())
 | |
|             {
 | |
|                 op.Property.SetValue(Convert.ChangeType(newValue, op.Property.PropertyType));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void OnGUI_Float(OptionDefinition op)
 | |
|         {
 | |
|             var range = op.Property.GetAttribute<NumberRangeAttribute>();
 | |
| 
 | |
|             float newValue;
 | |
| 
 | |
|             EditorGUI.BeginChangeCheck();
 | |
| 
 | |
|             if (range != null)
 | |
|             {
 | |
|                 newValue = EditorGUILayout.Slider(op.Name, (float)op.Property.GetValue(), (float)range.Min, (float)range.Max);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 newValue = EditorGUILayout.FloatField(op.Name, (float) op.Property.GetValue());
 | |
|             }
 | |
| 
 | |
|             if (EditorGUI.EndChangeCheck())
 | |
|             {
 | |
|                 op.Property.SetValue(Convert.ChangeType(newValue, op.Property.PropertyType));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void OnGUI_Double(OptionDefinition op)
 | |
|         {
 | |
|             var range = op.Property.GetAttribute<NumberRangeAttribute>();
 | |
| 
 | |
|             double newValue;
 | |
| 
 | |
|             EditorGUI.BeginChangeCheck();
 | |
| 
 | |
|             if (range != null && range.Min > float.MinValue && range.Max < float.MaxValue)
 | |
|             {
 | |
|                 newValue = EditorGUILayout.Slider(op.Name, (float)op.Property.GetValue(), (float)range.Min, (float)range.Max);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 newValue = EditorGUILayout.DoubleField(op.Name, (double) op.Property.GetValue());
 | |
| 
 | |
|                 if (range != null)
 | |
|                 {
 | |
|                     if (newValue > range.Max)
 | |
|                     {
 | |
|                         newValue = range.Max;
 | |
|                     } else if (newValue < range.Min)
 | |
|                     {
 | |
|                         newValue = range.Min;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (EditorGUI.EndChangeCheck())
 | |
|             {
 | |
|                 op.Property.SetValue(Convert.ChangeType(newValue, op.Property.PropertyType));
 | |
|             }
 | |
|         }
 | |
| 
 | |
| 
 | |
|         void OnGUI_AnyInteger(OptionDefinition op)
 | |
|         {
 | |
|             NumberControl.ValueRange range;
 | |
| 
 | |
|             if (!NumberControl.ValueRanges.TryGetValue(op.Property.PropertyType, out range))
 | |
|             {
 | |
|                 Debug.LogError("Unknown integer type: " + op.Property.PropertyType);
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             var userRange = op.Property.GetAttribute<NumberRangeAttribute>();
 | |
| 
 | |
|             EditorGUI.BeginChangeCheck();
 | |
| 
 | |
|             var oldValue = (long)Convert.ChangeType(op.Property.GetValue(), typeof(long));
 | |
|             var newValue = EditorGUILayout.LongField(op.Name, oldValue);
 | |
| 
 | |
|             if (newValue > range.MaxValue)
 | |
|             {
 | |
|                 newValue = (long)range.MaxValue;
 | |
|             } else if (newValue < range.MinValue)
 | |
|             {
 | |
|                 newValue = (long)range.MinValue;
 | |
|             }
 | |
| 
 | |
|             if (userRange != null)
 | |
|             {
 | |
|                 if (newValue > userRange.Max)
 | |
|                 {
 | |
|                     newValue = (long)userRange.Max;
 | |
|                 } else if (newValue < userRange.Min)
 | |
|                 {
 | |
|                     newValue = (long) userRange.Min;
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             if (EditorGUI.EndChangeCheck())
 | |
|             {
 | |
|                 op.Property.SetValue(Convert.ChangeType(newValue, op.Property.PropertyType));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void OnGUI_Unsupported(OptionDefinition op)
 | |
|         {
 | |
|             EditorGUILayout.PrefixLabel(op.Name);
 | |
|             EditorGUILayout.LabelField("Unsupported Type: {0}".Fmt(op.Property.PropertyType));
 | |
|         }
 | |
|     }
 | |
| }
 |