Neodroid  0.2.0
Machine Learning Environment Prototyping Tool
SerializableDictionaryDrawer.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using UnityEditor;
4 #if UNITY_EDITOR
5 using UnityEngine;
6 
7 namespace droid.Runtime.Utilities.ScriptableObjects.SerialisableDictionary {
8  public abstract class SerializableKeyValueTemplate<TK, TV> : ScriptableObject {
9  public TK _Key;
10  public TV _Value;
11  }
12 
13  public abstract class SerializableDictionaryDrawer<TK, TV> : PropertyDrawer {
14  Dictionary<int, Dictionary<int, SerializedProperty>> _indexed_property_dicts =
15  new Dictionary<int, Dictionary<int, SerializedProperty>>();
16 
17  Dictionary<int, SerializedProperty> _keys_props = new Dictionary<int, SerializedProperty>();
18 
19  Dictionary<int, SerializedProperty> _template_key_prop = new Dictionary<int, SerializedProperty>();
20 
21  Dictionary<int, SerializedProperty> _template_value_prop = new Dictionary<int, SerializedProperty>();
22 
23  Dictionary<int, SerializedProperty> _values_props = new Dictionary<int, SerializedProperty>();
24 
25  protected abstract SerializableKeyValueTemplate<TK, TV> GetTemplate();
26 
27  protected T GetGenericTemplate<T>() where T : SerializableKeyValueTemplate<TK, TV> {
28  return ScriptableObject.CreateInstance<T>();
29  }
30 
31  public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
32  EditorGUI.BeginProperty(position, label, property);
33 
34  var first_line = position;
35  first_line.height = EditorGUIUtility.singleLineHeight;
36  EditorGUI.PropertyField(first_line, property);
37 
38  if (property.isExpanded) {
39  var second_line = first_line;
40  second_line.y += EditorGUIUtility.singleLineHeight;
41 
42  EditorGUIUtility.labelWidth = 50f;
43 
44  second_line.x += 15f; // indentation
45  second_line.width -= 15f;
46 
47  var second_line_key = second_line;
48 
49  var button_width = 60f;
50  second_line_key.width -= button_width; // assign button
51  second_line_key.width /= 2f;
52 
53  var second_line_value = second_line_key;
54  second_line_value.x += second_line_value.width;
55  if (this.GetTemplateValueProp(property).hasVisibleChildren) {
56  // if the value has children, indent to make room for fold arrow
57  second_line_value.x += 15;
58  second_line_value.width -= 15;
59  }
60 
61  var second_line_button = second_line_value;
62  second_line_button.x += second_line_value.width;
63  second_line_button.width = button_width;
64 
65  var k_height = EditorGUI.GetPropertyHeight(this.GetTemplateKeyProp(property));
66  var v_height = EditorGUI.GetPropertyHeight(this.GetTemplateValueProp(property));
67  var extra_height = Mathf.Max(k_height, v_height);
68 
69  second_line_key.height = k_height;
70  second_line_value.height = v_height;
71 
72  EditorGUI.PropertyField(second_line_key, this.GetTemplateKeyProp(property), true);
73  EditorGUI.PropertyField(second_line_value, this.GetTemplateValueProp(property), true);
74 
75  var keys_prop = this.GetKeysProp(property);
76  var values_prop = this.GetValuesProp(property);
77 
78  var num_lines = keys_prop.arraySize;
79 
80  if (GUI.Button(second_line_button, "Assign")) {
81  var assignment = false;
82  for (var i = 0; i < num_lines; i++)
83  // Try to replace existing value
84  {
85  if (SerializedPropertyExtension.EqualBasics(this.GetIndexedItemProp(keys_prop, i),
86  this.GetTemplateKeyProp(property))) {
87  SerializedPropertyExtension.CopyBasics(this.GetTemplateValueProp(property),
88  this.GetIndexedItemProp(values_prop, i));
89  assignment = true;
90  break;
91  }
92  }
93 
94  if (!assignment) {
95  // Create a new value
96  keys_prop.arraySize += 1;
97  values_prop.arraySize += 1;
98  SerializedPropertyExtension.CopyBasics(this.GetTemplateKeyProp(property),
99  this.GetIndexedItemProp(keys_prop, num_lines));
100  SerializedPropertyExtension.CopyBasics(this.GetTemplateValueProp(property),
101  this.GetIndexedItemProp(values_prop, num_lines));
102  }
103  }
104 
105  for (var i = 0; i < num_lines; i++) {
106  second_line_key.y += extra_height;
107  second_line_value.y += extra_height;
108  second_line_button.y += extra_height;
109 
110  k_height = EditorGUI.GetPropertyHeight(this.GetIndexedItemProp(keys_prop, i));
111  v_height = EditorGUI.GetPropertyHeight(this.GetIndexedItemProp(values_prop, i));
112  extra_height = Mathf.Max(k_height, v_height);
113 
114  second_line_key.height = k_height;
115  second_line_value.height = v_height;
116 
117  EditorGUI.PropertyField(second_line_key, this.GetIndexedItemProp(keys_prop, i), true);
118  EditorGUI.PropertyField(second_line_value, this.GetIndexedItemProp(values_prop, i), true);
119 
120  if (GUI.Button(second_line_button, "Remove")) {
121  keys_prop.DeleteArrayElementAtIndex(i);
122  values_prop.DeleteArrayElementAtIndex(i);
123  }
124  }
125  }
126 
127  EditorGUI.EndProperty();
128  }
129 
130  public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
131  if (!property.isExpanded) {
132  return EditorGUIUtility.singleLineHeight;
133  }
134 
135  var total = EditorGUIUtility.singleLineHeight;
136 
137  var k_height = EditorGUI.GetPropertyHeight(this.GetTemplateKeyProp(property));
138  var v_height = EditorGUI.GetPropertyHeight(this.GetTemplateValueProp(property));
139  total += Mathf.Max(k_height, v_height);
140 
141  var keys_prop = this.GetKeysProp(property);
142  var values_prop = this.GetValuesProp(property);
143  var num_lines = keys_prop.arraySize;
144  for (var i = 0; i < num_lines; i++) {
145  k_height = EditorGUI.GetPropertyHeight(this.GetIndexedItemProp(keys_prop, i));
146  v_height = EditorGUI.GetPropertyHeight(this.GetIndexedItemProp(values_prop, i));
147  total += Mathf.Max(k_height, v_height);
148  }
149 
150  return total;
151  }
152 
153  SerializedProperty GetTemplateKeyProp(SerializedProperty main_prop) {
154  return this.GetTemplateProp(this._template_key_prop, main_prop);
155  }
156 
157  SerializedProperty GetTemplateValueProp(SerializedProperty main_prop) {
158  return this.GetTemplateProp(this._template_value_prop, main_prop);
159  }
160 
161  SerializedProperty GetTemplateProp(Dictionary<int, SerializedProperty> source,
162  SerializedProperty main_prop) {
163  if (!source.TryGetValue(main_prop.GetObjectCode(), out var p)) {
164  var template_object = this.GetTemplate();
165  var template_serialized_object = new SerializedObject(template_object);
166  var k_prop = template_serialized_object.FindProperty("key");
167  var v_prop = template_serialized_object.FindProperty("value");
168  this._template_key_prop[main_prop.GetObjectCode()] = k_prop;
169  this._template_value_prop[main_prop.GetObjectCode()] = v_prop;
170  p = source == this._template_key_prop ? k_prop : v_prop;
171  }
172 
173  return p;
174  }
175 
176  SerializedProperty GetKeysProp(SerializedProperty main_prop) {
177  return this.GetCachedProp(main_prop, "keys", this._keys_props);
178  }
179 
180  SerializedProperty GetValuesProp(SerializedProperty main_prop) {
181  return this.GetCachedProp(main_prop, "values", this._values_props);
182  }
183 
184  SerializedProperty GetCachedProp(SerializedProperty main_prop,
185  string relative_property_name,
186  Dictionary<int, SerializedProperty> source) {
187  var object_code = main_prop.GetObjectCode();
188  if (!source.TryGetValue(object_code, out var p)) {
189  source[object_code] = p = main_prop.FindPropertyRelative(relative_property_name);
190  }
191 
192  return p;
193  }
194 
195  SerializedProperty GetIndexedItemProp(SerializedProperty array_prop, int index) {
196  if (!this._indexed_property_dicts.TryGetValue(array_prop.GetObjectCode(), out var d)) {
197  this._indexed_property_dicts[array_prop.GetObjectCode()] =
198  d = new Dictionary<int, SerializedProperty>();
199  }
200 
201  if (!d.TryGetValue(index, out var result)) {
202  d[index] = result = array_prop.FindPropertyRelative($"Array.data[{index}]");
203  }
204 
205  return result;
206  }
207  }
208 }
209 #endif