com.wallstop-studios.unity-helpers 2.0.0-rc75.8 → 2.0.0-rc76

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.
Files changed (27) hide show
  1. package/Editor/CustomDrawers/IntDropdownDrawer.cs +46 -0
  2. package/Editor/CustomDrawers/IntDropdownDrawer.cs.meta +3 -0
  3. package/Editor/{StringInListeDrawer.cs → CustomDrawers/StringInListeDrawer.cs} +2 -2
  4. package/Editor/{WShowIfPropertyDrawer.cs → CustomDrawers/WShowIfPropertyDrawer.cs} +3 -3
  5. package/Editor/CustomDrawers.meta +3 -0
  6. package/Editor/Sprites/AnimationViewerWindow.cs +1521 -0
  7. package/Editor/Sprites/AnimationViewerWindow.cs.meta +3 -0
  8. package/Editor/Sprites/ProjectAnimationSettings.cs +50 -0
  9. package/Editor/Sprites/ProjectAnimationSettings.cs.meta +3 -0
  10. package/Editor/Sprites/ScriptableSpriteAtlas.cs +4 -2
  11. package/Editor/Sprites/SpriteSettingsApplier.cs +6 -0
  12. package/Editor/Styles/AnimationViewer.uss +116 -0
  13. package/Editor/Styles/AnimationViewer.uss.meta +3 -0
  14. package/Editor/Styles/AnimationViewer.uxml +57 -0
  15. package/Editor/Styles/AnimationViewer.uxml.meta +3 -0
  16. package/Editor/Styles.meta +3 -0
  17. package/Editor/UI.meta +3 -0
  18. package/Runtime/Core/Attributes/IntDropdownAttribute.cs +14 -0
  19. package/Runtime/Core/Attributes/IntDropdownAttribute.cs.meta +3 -0
  20. package/Runtime/UI/LayeredImage.cs +193 -98
  21. package/Runtime/UI/MultiFileSelectorElement.cs +322 -0
  22. package/Runtime/UI/MultiFileSelectorElement.cs.meta +3 -0
  23. package/package.json +3 -1
  24. package/Editor/AnimatorControllerCopier.cs +0 -156
  25. package/Editor/AnimatorControllerCopier.cs.meta +0 -3
  26. /package/Editor/{StringInListeDrawer.cs.meta → CustomDrawers/StringInListeDrawer.cs.meta} +0 -0
  27. /package/Editor/{WShowIfPropertyDrawer.cs.meta → CustomDrawers/WShowIfPropertyDrawer.cs.meta} +0 -0
@@ -0,0 +1,322 @@
1
+ namespace WallstopStudios.UnityHelpers.UI
2
+ {
3
+ using System;
4
+ using System.Collections.Generic;
5
+ using System.IO;
6
+ using System.Linq;
7
+ using Core.Extension;
8
+ using Core.Helper;
9
+ using UnityEngine;
10
+ using UnityEngine.UIElements;
11
+
12
+ public sealed class MultiFileSelectorElement : VisualElement
13
+ {
14
+ private const string DefaultRootPath = "Assets";
15
+
16
+ public event Action<List<string>> OnFilesSelected;
17
+ public event Action OnCancelled;
18
+
19
+ private readonly TextField _pathField;
20
+ private readonly Button _upButton;
21
+ private readonly VisualElement _fileListContent;
22
+ private readonly HashSet<string> _fileExtensionsToDisplay;
23
+ private readonly List<string> _selectedFilePaths = new();
24
+
25
+ private string _currentDirectory;
26
+
27
+ public MultiFileSelectorElement(string initialPath, string[] filterExtensions)
28
+ {
29
+ _fileExtensionsToDisplay =
30
+ filterExtensions?.Select(ext => ext).ToHashSet(StringComparer.OrdinalIgnoreCase)
31
+ ?? new HashSet<string>(StringComparer.OrdinalIgnoreCase);
32
+
33
+ style.position = Position.Absolute;
34
+ style.left = style.top = style.right = style.bottom = 0;
35
+ style.backgroundColor = new StyleColor(new Color(0.1f, 0.1f, 0.1f, 0.9f));
36
+ style.paddingLeft = style.paddingRight = style.paddingTop = style.paddingBottom = 20;
37
+ style.alignItems = Align.Center;
38
+ style.justifyContent = Justify.Center;
39
+
40
+ VisualElement contentBox = new()
41
+ {
42
+ style =
43
+ {
44
+ width = new Length(80, LengthUnit.Percent),
45
+ height = new Length(80, LengthUnit.Percent),
46
+ maxWidth = 700,
47
+ maxHeight = 500,
48
+ backgroundColor = new StyleColor(Color.gray),
49
+ paddingLeft = 10,
50
+ paddingRight = 10,
51
+ paddingTop = 10,
52
+ paddingBottom = 10,
53
+ flexDirection = FlexDirection.Column,
54
+ borderTopLeftRadius = 5,
55
+ borderTopRightRadius = 5,
56
+ borderBottomLeftRadius = 5,
57
+ borderBottomRightRadius = 5,
58
+ },
59
+ };
60
+ Add(contentBox);
61
+
62
+ VisualElement headerControls = new()
63
+ {
64
+ style =
65
+ {
66
+ flexDirection = FlexDirection.Row,
67
+ marginBottom = 5,
68
+ alignItems = Align.Center,
69
+ },
70
+ };
71
+ Button assetsFolderButton = new(() => NavigateTo(DefaultRootPath))
72
+ {
73
+ text = "Assets",
74
+ style = { width = 60, marginRight = 5 },
75
+ };
76
+ _upButton = new Button(NavigateUp)
77
+ {
78
+ text = "Up",
79
+ style = { width = 40, marginRight = 5 },
80
+ };
81
+ _pathField = new TextField(null)
82
+ {
83
+ isReadOnly = true,
84
+ style =
85
+ {
86
+ flexGrow = 1,
87
+ flexShrink = 1,
88
+ marginRight = 5,
89
+ },
90
+ };
91
+
92
+ headerControls.Add(assetsFolderButton);
93
+ headerControls.Add(_upButton);
94
+ headerControls.Add(_pathField);
95
+ contentBox.Add(headerControls);
96
+
97
+ // TODO: Custom styling
98
+ ScrollView fileListView = new(ScrollViewMode.Vertical)
99
+ {
100
+ style =
101
+ {
102
+ flexGrow = 1,
103
+ borderTopWidth = 1,
104
+ borderBottomWidth = 1,
105
+ borderLeftWidth = 1,
106
+ borderRightWidth = 1,
107
+ borderBottomColor = Color.black,
108
+ borderTopColor = Color.black,
109
+ borderLeftColor = Color.black,
110
+ borderRightColor = Color.black,
111
+ marginBottom = 5,
112
+ backgroundColor = Color.white * 0.15f,
113
+ },
114
+ };
115
+ _fileListContent = new VisualElement();
116
+ fileListView.Add(_fileListContent);
117
+ contentBox.Add(fileListView);
118
+
119
+ VisualElement footerControls = new()
120
+ {
121
+ style =
122
+ {
123
+ flexDirection = FlexDirection.Row,
124
+ justifyContent = Justify.FlexEnd,
125
+ marginTop = 5,
126
+ },
127
+ };
128
+ Button cancelButton = new(() => OnCancelled?.Invoke())
129
+ {
130
+ text = "Cancel",
131
+ style = { marginRight = 10 },
132
+ };
133
+ Button confirmButton = new(ConfirmSelection) { text = "Add Selected" };
134
+ footerControls.Add(cancelButton);
135
+ footerControls.Add(confirmButton);
136
+ contentBox.Add(footerControls);
137
+
138
+ string validInitialPath = initialPath;
139
+ if (
140
+ string.IsNullOrEmpty(validInitialPath)
141
+ || !Directory.Exists(Path.Combine(Application.dataPath, "..", validInitialPath))
142
+ )
143
+ {
144
+ validInitialPath = DefaultRootPath;
145
+ }
146
+ NavigateTo(validInitialPath);
147
+ }
148
+
149
+ private void NavigateTo(string path)
150
+ {
151
+ string fullPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", path));
152
+ string projectRootPath = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
153
+
154
+ if (!fullPath.StartsWith(projectRootPath, StringComparison.OrdinalIgnoreCase))
155
+ {
156
+ fullPath = projectRootPath;
157
+ }
158
+
159
+ string assetsFullPath = Path.GetFullPath(Application.dataPath);
160
+ if (
161
+ DefaultRootPath == "Assets"
162
+ && !fullPath.StartsWith(assetsFullPath, StringComparison.OrdinalIgnoreCase)
163
+ && fullPath.StartsWith(projectRootPath)
164
+ )
165
+ {
166
+ fullPath = assetsFullPath;
167
+ }
168
+
169
+ if (!Directory.Exists(fullPath))
170
+ {
171
+ Debug.LogWarning($"Directory does not exist: {fullPath}. Resetting to Assets.");
172
+ _currentDirectory = assetsFullPath;
173
+ }
174
+ else
175
+ {
176
+ _currentDirectory = fullPath;
177
+ }
178
+
179
+ if (_currentDirectory.Equals(projectRootPath, StringComparison.OrdinalIgnoreCase))
180
+ {
181
+ _pathField.SetValueWithoutNotify("Project Root");
182
+ }
183
+ else if (
184
+ _currentDirectory.StartsWith(projectRootPath, StringComparison.OrdinalIgnoreCase)
185
+ )
186
+ {
187
+ _pathField.SetValueWithoutNotify(
188
+ _currentDirectory.Substring(projectRootPath.Length + 1).SanitizePath()
189
+ );
190
+ }
191
+ else
192
+ {
193
+ _pathField.SetValueWithoutNotify(_currentDirectory.SanitizePath());
194
+ }
195
+
196
+ PopulateFileList();
197
+ DirectoryInfo parentDirectory = Directory.GetParent(_currentDirectory);
198
+ _upButton.SetEnabled(
199
+ !_currentDirectory.Equals(assetsFullPath, StringComparison.OrdinalIgnoreCase)
200
+ && parentDirectory != null
201
+ && parentDirectory.FullName.Length >= projectRootPath.Length
202
+ );
203
+ }
204
+
205
+ private void NavigateUp()
206
+ {
207
+ if (string.IsNullOrEmpty(_currentDirectory))
208
+ {
209
+ return;
210
+ }
211
+
212
+ DirectoryInfo directoryParent = Directory.GetParent(_currentDirectory);
213
+ if (directoryParent != null)
214
+ {
215
+ string projectRootPath = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
216
+
217
+ if (
218
+ directoryParent.FullName.Length < projectRootPath.Length
219
+ || _currentDirectory.Equals(
220
+ Path.GetFullPath(Application.dataPath),
221
+ StringComparison.OrdinalIgnoreCase
222
+ )
223
+ )
224
+ {
225
+ Debug.Log("Cannot navigate above Assets folder.");
226
+ NavigateTo(DefaultRootPath);
227
+ return;
228
+ }
229
+ NavigateTo(directoryParent.FullName.Substring(projectRootPath.Length + 1));
230
+ }
231
+ }
232
+
233
+ private void PopulateFileList()
234
+ {
235
+ _fileListContent.Clear();
236
+ try
237
+ {
238
+ foreach (string dirPath in Directory.GetDirectories(_currentDirectory).Ordered())
239
+ {
240
+ string dirName = Path.GetFileName(dirPath);
241
+ Button dirButton = new(() =>
242
+ NavigateTo(
243
+ dirPath.Substring(
244
+ Path.GetFullPath(Path.Combine(Application.dataPath, "..")).Length
245
+ + 1
246
+ )
247
+ )
248
+ )
249
+ {
250
+ text = $"📁 {dirName}",
251
+ style = { unityTextAlign = TextAnchor.MiddleLeft, marginBottom = 1 },
252
+ };
253
+ _fileListContent.Add(dirButton);
254
+ }
255
+
256
+ foreach (string filePath in Directory.GetFiles(_currentDirectory).Ordered())
257
+ {
258
+ if (_fileExtensionsToDisplay.Count > 0)
259
+ {
260
+ string extension = Path.GetExtension(filePath);
261
+ if (!_fileExtensionsToDisplay.Contains(extension))
262
+ {
263
+ continue;
264
+ }
265
+ }
266
+
267
+ VisualElement fileItem = new()
268
+ {
269
+ style =
270
+ {
271
+ flexDirection = FlexDirection.Row,
272
+ alignItems = Align.Center,
273
+ marginBottom = 1,
274
+ },
275
+ };
276
+ Toggle toggle = new()
277
+ {
278
+ value = _selectedFilePaths.Contains(filePath),
279
+ style = { marginRight = 5 },
280
+ };
281
+ toggle.RegisterValueChangedCallback(evt =>
282
+ {
283
+ if (evt.newValue)
284
+ {
285
+ _selectedFilePaths.Add(filePath);
286
+ }
287
+ else
288
+ {
289
+ _selectedFilePaths.Remove(filePath);
290
+ }
291
+ });
292
+
293
+ Label label = new(Path.GetFileName(filePath));
294
+ label.RegisterCallback<PointerDownEvent, Toggle>(
295
+ (_, context) => context.value = !context.value,
296
+ toggle
297
+ );
298
+
299
+ fileItem.Add(toggle);
300
+ fileItem.Add(label);
301
+ _fileListContent.Add(fileItem);
302
+ }
303
+ }
304
+ catch (Exception ex)
305
+ {
306
+ Debug.LogError($"Error accessing path {_currentDirectory}: {ex.Message}");
307
+ _fileListContent.Add(new Label($"Error reading directory: {ex.Message}"));
308
+ }
309
+ }
310
+
311
+ private void ConfirmSelection()
312
+ {
313
+ OnFilesSelected?.Invoke(_selectedFilePaths.ToList());
314
+ }
315
+
316
+ public void ResetAndShow(string newInitialPath)
317
+ {
318
+ _selectedFilePaths.Clear();
319
+ NavigateTo(newInitialPath);
320
+ }
321
+ }
322
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: b656837e77d2410291e9a1e224a1faae
3
+ timeCreated: 1748371754
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.wallstop-studios.unity-helpers",
3
- "version": "2.0.0-rc75.8",
3
+ "version": "2.0.0-rc76",
4
4
  "displayName": "Unity Helpers",
5
5
  "description": "Various Unity Helper Library",
6
6
  "dependencies": {},
@@ -71,6 +71,8 @@
71
71
 
72
72
 
73
73
 
74
+
75
+
74
76
 
75
77
 
76
78
 
@@ -1,156 +0,0 @@
1
- namespace WallstopStudios.UnityHelpers.Editor
2
- {
3
- #if UNITY_EDITOR
4
- using System;
5
- using System.IO;
6
- using UnityEditor;
7
- using UnityEngine;
8
- using Utils;
9
- using Core.Attributes;
10
- using Core.Extension;
11
-
12
- public sealed class AnimatorControllerCopier : ScriptableWizard
13
- {
14
- private string _fullSourcePath;
15
- private string _fullDestinationPath;
16
-
17
- [DxReadOnly]
18
- public string controllerSourcePath;
19
-
20
- [DxReadOnly]
21
- public string controllerDestinationPath;
22
-
23
- [MenuItem("Tools/Wallstop Studios/Unity Helpers/Animator Controller Copier")]
24
- public static void CopyAnimations()
25
- {
26
- _ = DisplayWizard<AnimatorControllerCopier>("Animator Controller Copier", "Copy");
27
- }
28
-
29
- protected override bool DrawWizardGUI()
30
- {
31
- bool returnValue = base.DrawWizardGUI();
32
-
33
- if (GUILayout.Button("Set Animator Controller Source Path"))
34
- {
35
- string sourcePath = EditorUtility.OpenFolderPanel(
36
- "Select Animator Controller Source Path",
37
- EditorUtilities.GetCurrentPathOfProjectWindow(),
38
- string.Empty
39
- );
40
- int assetIndex = sourcePath?.IndexOf("Assets", StringComparison.Ordinal) ?? -1;
41
- if (assetIndex < 0)
42
- {
43
- return false;
44
- }
45
-
46
- _fullSourcePath = controllerSourcePath = sourcePath ?? string.Empty;
47
- controllerSourcePath = controllerSourcePath.Substring(assetIndex);
48
- return true;
49
- }
50
-
51
- if (GUILayout.Button("Set Animator Controller Destination Path"))
52
- {
53
- string sourcePath = EditorUtility.OpenFolderPanel(
54
- "Select Animator Controller Destination Path",
55
- EditorUtilities.GetCurrentPathOfProjectWindow(),
56
- string.Empty
57
- );
58
- int assetIndex = sourcePath?.IndexOf("Assets", StringComparison.Ordinal) ?? -1;
59
- if (assetIndex < 0)
60
- {
61
- return false;
62
- }
63
-
64
- _fullDestinationPath = controllerDestinationPath = sourcePath ?? string.Empty;
65
- controllerDestinationPath = controllerDestinationPath.Substring(assetIndex);
66
- return true;
67
- }
68
-
69
- return returnValue;
70
- }
71
-
72
- private void OnWizardCreate()
73
- {
74
- if (string.IsNullOrEmpty(_fullSourcePath) || string.IsNullOrEmpty(_fullDestinationPath))
75
- {
76
- return;
77
- }
78
-
79
- if (
80
- string.IsNullOrEmpty(controllerSourcePath)
81
- || string.IsNullOrEmpty(controllerDestinationPath)
82
- )
83
- {
84
- return;
85
- }
86
-
87
- int processed = 0;
88
- foreach (
89
- string assetGuid in AssetDatabase.FindAssets(
90
- "t:AnimatorController",
91
- new[] { controllerSourcePath }
92
- )
93
- )
94
- {
95
- string path = AssetDatabase.GUIDToAssetPath(assetGuid);
96
-
97
- RuntimeAnimatorController animatorController =
98
- AssetDatabase.LoadAssetAtPath<RuntimeAnimatorController>(path);
99
- if (animatorController == null)
100
- {
101
- this.LogError(
102
- $"Invalid Animator Controller (null) found at path '{path}', skipping."
103
- );
104
- continue;
105
- }
106
-
107
- string prefix = animatorController.name;
108
- string relativePath = path.Substring(controllerSourcePath.Length);
109
- int prefixIndex = relativePath.LastIndexOf(
110
- prefix,
111
- StringComparison.OrdinalIgnoreCase
112
- );
113
- if (prefixIndex < 0)
114
- {
115
- this.LogWarn(
116
- $"Unsupported AnimatorController at '{path}', expected to be prefixed by '{prefix}'."
117
- );
118
- continue;
119
- }
120
-
121
- string partialPath = relativePath.Substring(0, prefixIndex);
122
- string outputPath = _fullDestinationPath + partialPath;
123
-
124
- if (!Directory.Exists(outputPath))
125
- {
126
- _ = Directory.CreateDirectory(outputPath);
127
- }
128
-
129
- string destination =
130
- controllerDestinationPath + partialPath + relativePath.Substring(prefixIndex);
131
- bool copySuccessful = AssetDatabase.CopyAsset(path, destination);
132
- if (copySuccessful)
133
- {
134
- bool deleteSuccessful = AssetDatabase.DeleteAsset(path);
135
- if (!deleteSuccessful)
136
- {
137
- this.LogError(
138
- $"Failed to delete Animator Controller asset at path '{path}'."
139
- );
140
- }
141
-
142
- ++processed;
143
- }
144
- else
145
- {
146
- this.LogError(
147
- $"Failed to copy Animator Controller from '{path}' to '{destination}'."
148
- );
149
- }
150
- }
151
-
152
- this.Log($"Processed {processed} Animator Controllers.");
153
- }
154
- }
155
- #endif
156
- }
@@ -1,3 +0,0 @@
1
- fileFormatVersion: 2
2
- guid: f0d39dce94454b8d9cb3fa0599ae02cc
3
- timeCreated: 1720824927