com.wallstop-studios.unity-helpers 2.0.0-rc76.6 → 2.0.0-rc76.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,133 @@
1
+ namespace WallstopStudios.UnityHelpers.Editor.AssetProcessors
2
+ {
3
+ #if UNITY_EDITOR
4
+ using System;
5
+ using System.Collections.Generic;
6
+ using System.Linq;
7
+ using Core.Extension;
8
+ using Core.Helper;
9
+ using UnityEditor;
10
+ using UnityEngine;
11
+ using Object = UnityEngine.Object;
12
+
13
+ public sealed class SpriteLabelProcessor : AssetPostprocessor
14
+ {
15
+ private static readonly Dictionary<string, string[]> CachedLabels = new(
16
+ StringComparer.OrdinalIgnoreCase
17
+ );
18
+
19
+ private static void OnPostprocessAllAssets(
20
+ string[] importedAssets,
21
+ string[] deletedAssets,
22
+ string[] movedAssets,
23
+ string[] movedFromAssetPaths
24
+ )
25
+ {
26
+ bool anyChanged = !CachedLabels.Any();
27
+ InitializeCacheIfNeeded();
28
+
29
+ foreach (string path in importedAssets)
30
+ {
31
+ if (
32
+ !path.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
33
+ && !path.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase)
34
+ && !path.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase)
35
+ )
36
+ {
37
+ continue;
38
+ }
39
+
40
+ TextureImporter ti = AssetImporter.GetAtPath(path) as TextureImporter;
41
+ if (ti == null || ti.textureType != TextureImporterType.Sprite)
42
+ {
43
+ continue;
44
+ }
45
+
46
+ Object mainObj = AssetDatabase.LoadMainAssetAtPath(path);
47
+ if (mainObj == null)
48
+ {
49
+ continue;
50
+ }
51
+
52
+ string[] newLabels = AssetDatabase.GetLabels(mainObj);
53
+ if (
54
+ !CachedLabels.TryGetValue(path, out string[] oldLabels)
55
+ || !AreEqual(oldLabels, newLabels)
56
+ )
57
+ {
58
+ Debug.Log(
59
+ $"[SpriteLabelProcessor] Labels changed on '{path}': {FormatLabels(oldLabels)} → {FormatLabels(newLabels)}"
60
+ );
61
+
62
+ string[] updated = new string[newLabels.Length];
63
+ Array.Copy(newLabels, updated, newLabels.Length);
64
+ anyChanged = true;
65
+ CachedLabels[path] = updated;
66
+ }
67
+ }
68
+
69
+ if (anyChanged)
70
+ {
71
+ Helpers.AllSpriteLabels = CachedLabels
72
+ .Values.SelectMany(x => x)
73
+ .Distinct()
74
+ .Ordered()
75
+ .ToArray();
76
+ }
77
+ }
78
+
79
+ private static void InitializeCacheIfNeeded()
80
+ {
81
+ if (CachedLabels.Count > 0)
82
+ {
83
+ return;
84
+ }
85
+
86
+ string[] guids = AssetDatabase.FindAssets("t:Sprite");
87
+ foreach (string guid in guids)
88
+ {
89
+ string path = AssetDatabase.GUIDToAssetPath(guid);
90
+ Object asset = AssetDatabase.LoadMainAssetAtPath(path);
91
+ if (asset == null)
92
+ {
93
+ continue;
94
+ }
95
+
96
+ CachedLabels[path] = AssetDatabase.GetLabels(asset);
97
+ }
98
+ }
99
+
100
+ private static bool AreEqual(string[] a, string[] b)
101
+ {
102
+ if (a == null && b == null)
103
+ {
104
+ return true;
105
+ }
106
+
107
+ if (a == null || b == null)
108
+ {
109
+ return false;
110
+ }
111
+
112
+ if (a.Length != b.Length)
113
+ {
114
+ return false;
115
+ }
116
+
117
+ HashSet<string> setA = new(a, StringComparer.OrdinalIgnoreCase);
118
+ HashSet<string> setB = new(b, StringComparer.OrdinalIgnoreCase);
119
+ return setA.SetEquals(setB);
120
+ }
121
+
122
+ private static string FormatLabels(string[] arr)
123
+ {
124
+ if (arr == null || arr.Length == 0)
125
+ {
126
+ return "(none)";
127
+ }
128
+
129
+ return string.Join(", ", arr);
130
+ }
131
+ }
132
+ #endif
133
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 36d579d02fe04cb3baab70ec40645cc9
3
+ timeCreated: 1748731957
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 46f7f82eccd54399947ba07a548e8a87
3
+ timeCreated: 1748731948
@@ -4,12 +4,25 @@
4
4
  using System;
5
5
  using UnityEditor;
6
6
  using UnityEngine;
7
- using WallstopStudios.UnityHelpers.Core.Helper;
7
+ using Core.Helper;
8
8
 
9
9
  [CustomPropertyDrawer(typeof(StringInList))]
10
- public class StringInListDrawer : PropertyDrawer
10
+ public sealed class StringInListDrawer : PropertyDrawer
11
11
  {
12
- // Draw the property inside the given rect
12
+ public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
13
+ {
14
+ if (property.isArray && property.propertyType == SerializedPropertyType.Generic)
15
+ {
16
+ int arraySize = property.arraySize;
17
+
18
+ float singleLine = EditorGUIUtility.singleLineHeight;
19
+ float spacing = EditorGUIUtility.standardVerticalSpacing;
20
+ return singleLine + arraySize * (singleLine + spacing);
21
+ }
22
+
23
+ return base.GetPropertyHeight(property, label);
24
+ }
25
+
13
26
  public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
14
27
  {
15
28
  if (attribute is not StringInList stringInList)
@@ -19,37 +32,103 @@
19
32
 
20
33
  string[] list = stringInList.List;
21
34
 
22
- switch (property.propertyType)
35
+ if (property.propertyType == SerializedPropertyType.String)
23
36
  {
24
- case SerializedPropertyType.String:
37
+ int index = Mathf.Max(0, Array.IndexOf(list, property.stringValue));
38
+ index = EditorGUI.Popup(position, property.displayName, index, list);
39
+ if (index < 0 || list.Length <= index)
25
40
  {
26
- int index = Mathf.Max(0, Array.IndexOf(list, property.stringValue));
27
- index = EditorGUI.Popup(position, property.displayName, index, list);
28
- if (index < 0 || list.Length <= index)
29
- {
30
- base.OnGUI(position, property, label);
31
- return;
32
- }
33
-
34
- property.stringValue = list[index];
35
- break;
41
+ base.OnGUI(position, property, label);
42
+ return;
36
43
  }
37
- case SerializedPropertyType.Integer:
44
+
45
+ property.stringValue = list[index];
46
+ }
47
+ else if (property.propertyType == SerializedPropertyType.Integer)
48
+ {
49
+ property.intValue = EditorGUI.Popup(
50
+ position,
51
+ property.displayName,
52
+ property.intValue,
53
+ list
54
+ );
55
+ }
56
+ else if (property.isArray && property.propertyType == SerializedPropertyType.Generic)
57
+ {
58
+ EditorGUI.BeginProperty(position, label, property);
59
+
60
+ int originalIndent = EditorGUI.indentLevel;
61
+ EditorGUI.indentLevel++;
62
+ try
38
63
  {
39
- property.intValue = EditorGUI.Popup(
40
- position,
41
- property.displayName,
42
- property.intValue,
43
- list
64
+ Rect sizeRect = new(
65
+ position.x,
66
+ position.y,
67
+ position.width,
68
+ EditorGUIUtility.singleLineHeight
44
69
  );
45
- break;
70
+ int newSize = EditorGUI.IntField(
71
+ sizeRect,
72
+ property.displayName + " Size",
73
+ property.arraySize
74
+ );
75
+ if (newSize < 0)
76
+ {
77
+ newSize = 0;
78
+ }
79
+
80
+ if (newSize != property.arraySize)
81
+ {
82
+ property.arraySize = newSize;
83
+ }
84
+
85
+ for (int i = 0; i < property.arraySize; i++)
86
+ {
87
+ SerializedProperty elemProp = property.GetArrayElementAtIndex(i);
88
+ Rect elementRect = new(
89
+ position.x,
90
+ position.y
91
+ + (
92
+ EditorGUIUtility.singleLineHeight
93
+ + EditorGUIUtility.standardVerticalSpacing
94
+ ) * (i + 1),
95
+ position.width,
96
+ EditorGUIUtility.singleLineHeight
97
+ );
98
+
99
+ if (elemProp.propertyType == SerializedPropertyType.String)
100
+ {
101
+ int currentIndex = Mathf.Max(
102
+ 0,
103
+ Array.IndexOf(list, elemProp.stringValue)
104
+ );
105
+ currentIndex = EditorGUI.Popup(
106
+ elementRect,
107
+ $"Element {i}",
108
+ currentIndex,
109
+ list
110
+ );
111
+ if (currentIndex >= 0 && currentIndex < list.Length)
112
+ {
113
+ elemProp.stringValue = list[currentIndex];
114
+ }
115
+ }
116
+ else
117
+ {
118
+ EditorGUI.PropertyField(elementRect, elemProp);
119
+ }
120
+ }
46
121
  }
47
- default:
122
+ finally
48
123
  {
49
- base.OnGUI(position, property, label);
50
- break;
124
+ EditorGUI.indentLevel = originalIndent;
125
+ EditorGUI.EndProperty();
51
126
  }
52
127
  }
128
+ else
129
+ {
130
+ base.OnGUI(position, property, label);
131
+ }
53
132
  }
54
133
  }
55
134
  #endif
@@ -1,12 +1,12 @@
1
1
  namespace WallstopStudios.UnityHelpers.Editor.CustomDrawers
2
2
  {
3
3
  #if UNITY_EDITOR
4
+ using System;
4
5
  using System.Reflection;
5
6
  using Extensions;
6
7
  using UnityEditor;
7
8
  using UnityEngine;
8
- using WallstopStudios.UnityHelpers.Core.Attributes;
9
- using WallstopStudios.UnityHelpers.Core.Extension;
9
+ using Core.Attributes;
10
10
 
11
11
  [CustomPropertyDrawer(typeof(WShowIfAttribute))]
12
12
  public sealed class WShowIfPropertyDrawer : PropertyDrawer
@@ -49,11 +49,19 @@
49
49
  showIf.conditionField,
50
50
  BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
51
51
  );
52
- if (conditionField?.GetValue(enclosingObject) is bool maybeCondition)
52
+ object fieldValue = conditionField?.GetValue(enclosingObject);
53
+ if (fieldValue is bool maybeCondition)
53
54
  {
54
55
  return showIf.inverse ? !maybeCondition : maybeCondition;
55
56
  }
56
- return true;
57
+
58
+ int index = Array.IndexOf(showIf.expectedValues, fieldValue);
59
+ if (showIf.inverse)
60
+ {
61
+ return index < 0;
62
+ }
63
+
64
+ return 0 <= index;
57
65
  }
58
66
 
59
67
  bool condition = conditionProperty.boolValue;
@@ -131,83 +131,165 @@
131
131
  );
132
132
 
133
133
  currentY += EditorGUIUtility.standardVerticalSpacing;
134
- Rect regexFoldoutLabelRect = new(
134
+
135
+ SerializedProperty modeProp = property.FindPropertyRelative(
136
+ nameof(SourceFolderEntry.selectionMode)
137
+ );
138
+ SpriteSelectionMode modeValue = (SpriteSelectionMode)modeProp.intValue;
139
+ Rect selectionMode = new(
135
140
  startX,
136
141
  currentY,
137
142
  availableWidth,
138
143
  EditorGUIUtility.singleLineHeight
139
144
  );
145
+ modeValue = (SpriteSelectionMode)
146
+ EditorGUI.EnumFlagsField(
147
+ selectionMode,
148
+ new GUIContent("Selection Mode"),
149
+ modeValue
150
+ );
151
+ modeProp.intValue = (int)modeValue;
152
+ currentY +=
153
+ EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
140
154
 
141
- string regexesFoldoutKey = GetRegexFoldoutKey(property);
142
- RegexesFoldoutState.TryAdd(regexesFoldoutKey, true);
143
- RegexesFoldoutState[regexesFoldoutKey] = EditorGUI.Foldout(
144
- regexFoldoutLabelRect,
145
- RegexesFoldoutState[regexesFoldoutKey],
146
- "Regexes (AND logic)",
147
- true
148
- );
149
- currentY += regexFoldoutLabelRect.height + EditorGUIUtility.standardVerticalSpacing;
155
+ bool useRegex = (modeValue & SpriteSelectionMode.Regex) != 0;
156
+ bool useLabels = (modeValue & SpriteSelectionMode.Labels) != 0;
150
157
 
151
- if (RegexesFoldoutState[regexesFoldoutKey])
158
+ if (useRegex)
152
159
  {
153
- SerializedProperty regexesProp = property.FindPropertyRelative(
154
- nameof(SourceFolderEntry.regexes)
160
+ Rect regexFoldoutLabelRect = new(
161
+ startX,
162
+ currentY,
163
+ availableWidth,
164
+ EditorGUIUtility.singleLineHeight
165
+ );
166
+
167
+ string regexesFoldoutKey = GetRegexFoldoutKey(property);
168
+ RegexesFoldoutState.TryAdd(regexesFoldoutKey, true);
169
+ RegexesFoldoutState[regexesFoldoutKey] = EditorGUI.Foldout(
170
+ regexFoldoutLabelRect,
171
+ RegexesFoldoutState[regexesFoldoutKey],
172
+ "Regexes (AND logic)",
173
+ true
155
174
  );
156
- float regexStartX = startX + 15f;
157
- float regexWidth = availableWidth - 15f;
175
+ currentY +=
176
+ regexFoldoutLabelRect.height + EditorGUIUtility.standardVerticalSpacing;
158
177
 
159
- for (int i = 0; i < regexesProp.arraySize; i++)
178
+ if (RegexesFoldoutState[regexesFoldoutKey])
160
179
  {
161
- SerializedProperty elemProp = regexesProp.GetArrayElementAtIndex(i);
162
- Rect fieldRect = new(
163
- regexStartX,
164
- currentY,
165
- regexWidth - 25f,
166
- EditorGUIUtility.singleLineHeight
180
+ SerializedProperty regexesProp = property.FindPropertyRelative(
181
+ nameof(SourceFolderEntry.regexes)
167
182
  );
168
- EditorGUI.BeginChangeCheck();
169
- string newVal = EditorGUI.TextField(
170
- fieldRect,
171
- $"Regex {i}:",
172
- elemProp.stringValue
173
- );
174
- if (EditorGUI.EndChangeCheck())
183
+ float regexStartX = startX + 15f;
184
+ float regexWidth = availableWidth - 15f;
185
+
186
+ for (int i = 0; i < regexesProp.arraySize; i++)
175
187
  {
176
- elemProp.stringValue = newVal;
188
+ SerializedProperty elemProp = regexesProp.GetArrayElementAtIndex(i);
189
+ Rect fieldRect = new(
190
+ regexStartX,
191
+ currentY,
192
+ regexWidth - 25f,
193
+ EditorGUIUtility.singleLineHeight
194
+ );
195
+ EditorGUI.BeginChangeCheck();
196
+ string newVal = EditorGUI.TextField(
197
+ fieldRect,
198
+ $"Regex {i}:",
199
+ elemProp.stringValue
200
+ );
201
+ if (EditorGUI.EndChangeCheck())
202
+ {
203
+ elemProp.stringValue = newVal;
204
+ }
205
+
206
+ Rect remRect = new(
207
+ fieldRect.xMax + 4f,
208
+ currentY,
209
+ 25f,
210
+ EditorGUIUtility.singleLineHeight
211
+ );
212
+ if (GUI.Button(remRect, "–"))
213
+ {
214
+ regexesProp.DeleteArrayElementAtIndex(i);
215
+ property.serializedObject.ApplyModifiedProperties();
216
+ }
217
+
218
+ currentY +=
219
+ EditorGUIUtility.singleLineHeight
220
+ + EditorGUIUtility.standardVerticalSpacing;
177
221
  }
178
222
 
179
- Rect remRect = new(
180
- fieldRect.xMax + 4f,
223
+ Rect addRect = new(
224
+ regexStartX,
181
225
  currentY,
182
- 25f,
226
+ regexWidth,
183
227
  EditorGUIUtility.singleLineHeight
184
228
  );
185
- if (GUI.Button(remRect, "–"))
229
+
230
+ if (GUI.Button(addRect, "+ Add Regex"))
186
231
  {
187
- regexesProp.DeleteArrayElementAtIndex(i);
232
+ int idx = regexesProp.arraySize;
233
+ regexesProp.InsertArrayElementAtIndex(idx);
234
+ regexesProp.GetArrayElementAtIndex(idx).stringValue = string.Empty;
188
235
  property.serializedObject.ApplyModifiedProperties();
189
236
  }
237
+ }
238
+ }
190
239
 
191
- currentY +=
240
+ if (useRegex && useLabels)
241
+ {
242
+ SerializedProperty booleanProp = property.FindPropertyRelative(
243
+ nameof(SourceFolderEntry.regexAndTagLogic)
244
+ );
245
+ currentY +=
246
+ EditorGUIUtility.singleLineHeight
247
+ + EditorGUIUtility.standardVerticalSpacing;
248
+ EditorGUI.PropertyField(
249
+ new Rect(
250
+ startX,
251
+ currentY,
252
+ availableWidth,
192
253
  EditorGUIUtility.singleLineHeight
193
- + EditorGUIUtility.standardVerticalSpacing;
194
- }
254
+ ),
255
+ booleanProp,
256
+ new GUIContent("Regex & Tags Logic")
257
+ );
258
+ currentY +=
259
+ EditorGUIUtility.singleLineHeight
260
+ + EditorGUIUtility.standardVerticalSpacing;
261
+ }
195
262
 
196
- Rect addRect = new(
197
- regexStartX,
263
+ if (useLabels)
264
+ {
265
+ SerializedProperty labelModeProp = property.FindPropertyRelative(
266
+ "labelSelectionMode"
267
+ );
268
+ Rect rectLabelMode = new(
269
+ startX,
198
270
  currentY,
199
- regexWidth,
271
+ availableWidth,
200
272
  EditorGUIUtility.singleLineHeight
201
273
  );
274
+ EditorGUI.PropertyField(
275
+ rectLabelMode,
276
+ labelModeProp,
277
+ new GUIContent("Label Selection Mode")
278
+ );
279
+ currentY +=
280
+ EditorGUIUtility.singleLineHeight
281
+ + EditorGUIUtility.standardVerticalSpacing;
202
282
 
203
- if (GUI.Button(addRect, "+ Add Regex"))
204
- {
205
- int idx = regexesProp.arraySize;
206
- regexesProp.InsertArrayElementAtIndex(idx);
207
- regexesProp.GetArrayElementAtIndex(idx).stringValue = string.Empty;
208
- property.serializedObject.ApplyModifiedProperties();
209
- }
283
+ SerializedProperty labelsProp = property.FindPropertyRelative(
284
+ nameof(SourceFolderEntry.labels)
285
+ );
286
+ float labelsHeight = EditorGUI.GetPropertyHeight(labelsProp, true);
287
+
288
+ Rect rectLabels = new(startX, currentY, availableWidth, labelsHeight);
289
+ EditorGUI.PropertyField(rectLabels, labelsProp, new GUIContent("Labels"), true);
290
+ currentY += labelsHeight + EditorGUIUtility.standardVerticalSpacing;
210
291
  }
292
+
211
293
  EditorGUI.indentLevel = originalIndent;
212
294
  }
213
295
  EditorGUI.EndProperty();
@@ -237,19 +319,62 @@
237
319
 
238
320
  height += EditorGUIUtility.standardVerticalSpacing;
239
321
  height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
240
- string regexesFoldoutKey = GetRegexFoldoutKey(property);
241
- bool isRegexesExpanded = RegexesFoldoutState.GetValueOrDefault(regexesFoldoutKey, true);
242
- if (isRegexesExpanded)
322
+
323
+ SerializedProperty modeProp = property.FindPropertyRelative(
324
+ nameof(SourceFolderEntry.selectionMode)
325
+ );
326
+ SpriteSelectionMode modeValue = (SpriteSelectionMode)modeProp.intValue;
327
+ bool useRegex = (modeValue & SpriteSelectionMode.Regex) != 0;
328
+ bool useLabels = (modeValue & SpriteSelectionMode.Labels) != 0;
329
+
330
+ if (useRegex)
243
331
  {
244
- SerializedProperty regexesProp = property.FindPropertyRelative(
245
- nameof(SourceFolderEntry.regexes)
332
+ string regexesFoldoutKey = GetRegexFoldoutKey(property);
333
+ bool isRegexesExpanded = RegexesFoldoutState.GetValueOrDefault(
334
+ regexesFoldoutKey,
335
+ true
246
336
  );
247
- height +=
248
- (1 + regexesProp.arraySize)
249
- * (
250
- EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing
337
+ if (isRegexesExpanded)
338
+ {
339
+ SerializedProperty regexesProp = property.FindPropertyRelative(
340
+ nameof(SourceFolderEntry.regexes)
251
341
  );
342
+ height +=
343
+ (1 + regexesProp.arraySize)
344
+ * (
345
+ EditorGUIUtility.singleLineHeight
346
+ + EditorGUIUtility.standardVerticalSpacing
347
+ );
348
+ }
349
+
350
+ height +=
351
+ EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
352
+ }
353
+
354
+ if (useRegex && useLabels)
355
+ {
356
+ height +=
357
+ EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
252
358
  }
359
+
360
+ if (useLabels)
361
+ {
362
+ // 1) Draw the “Label Selection Mode” line (dropdown)
363
+ height += EditorGUIUtility.singleLineHeight;
364
+ height += EditorGUIUtility.standardVerticalSpacing;
365
+
366
+ // 2) Now figure out how tall “labels” really is. Let Unity handle foldout‐vs‐expanded.
367
+ SerializedProperty labelsProp = property.FindPropertyRelative(
368
+ nameof(SourceFolderEntry.labels)
369
+ );
370
+
371
+ // Passing `true` tells Unity: “Include children if expanded, or just header if collapsed.”
372
+ float labelsFullHeight = EditorGUI.GetPropertyHeight(labelsProp, true);
373
+
374
+ height += labelsFullHeight;
375
+ height += EditorGUIUtility.standardVerticalSpacing;
376
+ }
377
+
253
378
  height += EditorGUIUtility.standardVerticalSpacing;
254
379
  return height;
255
380
  }
@@ -27,6 +27,17 @@
27
27
  Type type = obj.GetType();
28
28
  string[] pathParts = property.propertyPath.Split('.');
29
29
 
30
+ if (
31
+ string.Equals(property.name, "data", StringComparison.Ordinal)
32
+ && pathParts.Length > 1
33
+ && pathParts[^1].Contains('[')
34
+ && pathParts[^1].Contains(']')
35
+ && string.Equals(pathParts[^2], "Array", StringComparison.Ordinal)
36
+ )
37
+ {
38
+ Array.Resize(ref pathParts, pathParts.Length - 2);
39
+ }
40
+
30
41
  // Traverse the path but stop at the second-to-last field
31
42
  for (int i = 0; i < pathParts.Length - 1; ++i)
32
43
  {