A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.IO;
4using UnityEngine;
5using UnityEngine.U2D.Animation;
6
7namespace UnityEditor.U2D.Animation.SpriteLibraryEditor
8{
9 internal enum ActionType
10 {
11 SelectCategory,
12 SelectLabels,
13 RenameCategory,
14 RenameLabel,
15 CreateCategory,
16 CreateLabel,
17 DeleteCategories,
18 DeleteLabels,
19 ReorderCategories,
20 ReorderLabels,
21 ModifiedCategories,
22 ModifiedLabels,
23
24 SetMainLibrary,
25 SetLabelSprite,
26
27 None
28 }
29
30 internal enum ViewType
31 {
32 List,
33 Grid
34 }
35
36 internal struct ViewData
37 {
38 /// <summary>
39 /// View type (List / Grid).
40 /// </summary>
41 public ViewType viewType;
42
43 /// <summary>
44 /// Relative size for selected View Type in range 0-1.
45 /// </summary>
46 public float relativeSize;
47
48 /// <summary>
49 /// Absolute size based on the slider position in range 0-1.
50 /// </summary>
51 public float absoluteSize;
52 }
53
54 internal enum SearchType
55 {
56 CategoryAndLabel,
57 Category,
58 Label
59 }
60
61 internal class WindowController
62 {
63 ISpriteLibraryEditorWindow m_Window;
64 SpriteLibraryEditorModel m_Model;
65
66 List<string> selectedCategories => m_Model.GetSelectedCategories();
67 List<string> selectedLabels => m_Model.GetSelectedLabels();
68
69 public SpriteLibraryAsset GetSelectedAsset() => m_Model.selectedAsset;
70
71 ControllerEvents m_ControllerEvents;
72 ViewEvents m_ViewEvents;
73
74 bool hasSelectedLibrary => m_Model.selectedAsset != null;
75
76 const float k_ViewSizeDivision = 0.1f;
77 float m_ViewSize;
78 ViewType m_ViewType = ViewType.List;
79
80 string m_FilterString = "";
81 SearchType m_FilterType = SearchType.CategoryAndLabel;
82
83 string m_SelectedAssetPath;
84
85 const string k_DefaultLabelName = "New Label";
86 const string k_DefaultCategoryName = "New Category";
87
88 bool m_AutoSave;
89 bool m_SelectionLocked;
90
91 public WindowController(ISpriteLibraryEditorWindow window, SpriteLibraryEditorModel model, ControllerEvents controllerEvents, ViewEvents viewEvents)
92 {
93 m_Window = window;
94 m_ControllerEvents = controllerEvents;
95 m_ViewEvents = viewEvents;
96
97 m_Model = model;
98
99 AddAssetPostprocessorListeners();
100 AddViewEventListeners();
101
102 Selection.selectionChanged += SelectionChanged;
103 Undo.undoRedoPerformed += PropagateLastAction;
104
105 m_AutoSave = SpriteLibraryEditorWindow.Settings.autoSave;
106 }
107
108 public void Destroy()
109 {
110 RemoveAssetPostprocessorListeners();
111
112 Selection.selectionChanged -= SelectionChanged;
113 Undo.undoRedoPerformed -= PropagateLastAction;
114
115 if (m_Model != null)
116 m_Model.Destroy();
117 }
118
119 public void SaveChanges()
120 {
121 m_Model.SaveLibrary(m_SelectedAssetPath);
122
123 m_ControllerEvents.onSelectedLibrary?.Invoke(m_Model.selectedAsset);
124 }
125
126 public void RevertChanges()
127 {
128 m_Model.SelectLabels(new List<string>());
129 m_Model.SelectCategories(new List<string>());
130 m_Model.SelectAsset(m_Model.selectedAsset);
131
132 m_ControllerEvents.onSelectedLibrary?.Invoke(m_Model.selectedAsset);
133 RefreshView();
134 RefreshSelection();
135 }
136
137 public void SelectAsset(SpriteLibraryAsset asset, bool modifiedExternally = false)
138 {
139 // Only allow .spriteLib files to be used in the editor.
140 // This is to prevent the user from opening the editor with .psd or .psb containing spriteLibs.
141 if (!AssetDatabase.GetAssetPath(asset).EndsWith(".spriteLib"))
142 return;
143
144 if (!modifiedExternally)
145 {
146 if (asset == null || asset == m_Model.selectedAsset)
147 return;
148
149 if (m_Window.hasUnsavedChanges)
150 m_Window.HandleUnsavedChanges();
151 }
152
153 m_SelectedAssetPath = asset != null ? AssetDatabase.GetAssetPath(asset) : null;
154
155 m_Model.SelectAsset(asset);
156
157 m_ControllerEvents.onSelectedLibrary?.Invoke(asset);
158
159 RefreshView();
160 RefreshSelection();
161 }
162
163 void AddViewEventListeners()
164 {
165 m_ViewEvents.onCreateNewSpriteLibraryAsset.AddListener(CreateNewSpriteLibraryAsset);
166 m_ViewEvents.onSave.AddListener(OnSave);
167 m_ViewEvents.onRevert.AddListener(OnRevert);
168 m_ViewEvents.onToggleAutoSave.AddListener(ToggleAutoSave);
169 m_ViewEvents.onToggleSelectionLock.AddListener(ToggleSelectionLock);
170
171 m_ViewEvents.onViewSizeUpdate.AddListener(ChangeViewSize);
172 m_ViewEvents.onViewTypeUpdate.AddListener(ChangeViewType);
173
174 m_ViewEvents.onSelectedFilter.AddListener(SelectedFilter);
175 m_ViewEvents.onSelectedFilterType.AddListener(SelectedFilterType);
176
177 m_ViewEvents.onSetMainAsset.AddListener(SetMainAsset);
178 m_ViewEvents.onSelectCategories.AddListener(SelectCategories);
179 m_ViewEvents.onSelectLabels.AddListener(SelectLabels);
180
181 m_ViewEvents.onCreateNewCategory.AddListener(CreateNewCategory);
182 m_ViewEvents.onRenameCategory.AddListener(RenameSelectedCategory);
183 m_ViewEvents.onReorderCategories.AddListener(ReorderCategories);
184 m_ViewEvents.onDeleteCategories.AddListener(DeleteSelectedCategories);
185
186 m_ViewEvents.onCreateNewLabel.AddListener(CreateNewLabel);
187 m_ViewEvents.onRenameLabel.AddListener(RenameSelectedLabel);
188 m_ViewEvents.onReorderLabels.AddListener(ReorderLabels);
189 m_ViewEvents.onDeleteLabels.AddListener(DeleteSelectedLabels);
190 m_ViewEvents.onSetLabelSprite.AddListener(SetLabelSprite);
191
192 m_ViewEvents.onAddDataToCategories.AddListener(AddDataToCategories);
193 m_ViewEvents.onAddDataToLabels.AddListener(AddDataToLabels);
194 m_ViewEvents.onRevertOverridenLabels.AddListener(RevertOverridenLabels);
195 }
196
197 void AddAssetPostprocessorListeners()
198 {
199 SpriteLibraryAssetPostprocessor.OnImported += OnAssetModified;
200 SpriteLibraryAssetPostprocessor.OnDeleted += OnAssetModified;
201 SpriteLibraryAssetPostprocessor.OnMovedAssetFromTo += OnAssetMoved;
202 }
203
204 void RemoveAssetPostprocessorListeners()
205 {
206 SpriteLibraryAssetPostprocessor.OnImported -= OnAssetModified;
207 SpriteLibraryAssetPostprocessor.OnDeleted -= OnAssetModified;
208 SpriteLibraryAssetPostprocessor.OnMovedAssetFromTo -= OnAssetMoved;
209 }
210
211 static void CreateNewSpriteLibraryAsset(string newAssetPath)
212 {
213 if (string.IsNullOrEmpty(newAssetPath) || !string.Equals(Path.GetExtension(newAssetPath), SpriteLibrarySourceAsset.extension, StringComparison.OrdinalIgnoreCase))
214 return;
215
216 // Make sure that the extension is exactly the same
217 if (Path.GetExtension(newAssetPath) != SpriteLibrarySourceAsset.extension)
218 newAssetPath = newAssetPath.Replace(Path.GetExtension(newAssetPath), SpriteLibrarySourceAsset.extension);
219
220 var assetToSave = ScriptableObject.CreateInstance<SpriteLibrarySourceAsset>();
221 SpriteLibrarySourceAssetImporter.SaveSpriteLibrarySourceAsset(assetToSave, newAssetPath);
222 AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
223
224 var newAsset = AssetDatabase.LoadAssetAtPath<SpriteLibraryAsset>(newAssetPath);
225 Selection.objects = new UnityEngine.Object[] { newAsset };
226 }
227
228 void ChangeViewSize(float newSize)
229 {
230 m_ViewSize = newSize;
231 if (m_ViewSize > k_ViewSizeDivision && m_ViewType == ViewType.List)
232 m_ViewType = ViewType.Grid;
233 if (m_ViewSize < k_ViewSizeDivision && m_ViewType == ViewType.Grid)
234 m_ViewType = ViewType.List;
235
236 m_ControllerEvents.onViewChanged?.Invoke(new ViewData { viewType = m_ViewType, relativeSize = GetAdjustedViewSize(m_ViewSize, m_ViewType), absoluteSize = m_ViewSize });
237 }
238
239 void ChangeViewType(ViewType viewType)
240 {
241 if (m_ViewType == viewType)
242 return;
243
244 m_ViewType = viewType;
245 m_ViewSize = m_ViewType == ViewType.List ? 0.0f : k_ViewSizeDivision;
246
247 m_ControllerEvents.onViewChanged?.Invoke(new ViewData { viewType = m_ViewType, relativeSize = GetAdjustedViewSize(m_ViewSize, m_ViewType), absoluteSize = m_ViewSize });
248 }
249
250 static float GetAdjustedViewSize(float size, ViewType viewType)
251 {
252 if (viewType == ViewType.List)
253 return size / k_ViewSizeDivision;
254 return (size - k_ViewSizeDivision) / (1 - k_ViewSizeDivision);
255 }
256
257 void SelectedFilterType(SearchType newFilterType)
258 {
259 if (m_FilterType == newFilterType)
260 return;
261
262 m_FilterType = newFilterType;
263
264 RefreshView();
265 }
266
267 void SelectedFilter(string newFilterString)
268 {
269 if (string.Equals(m_FilterString, newFilterString, StringComparison.OrdinalIgnoreCase))
270 return;
271
272 m_FilterString = newFilterString;
273
274 RefreshView();
275 }
276
277 void OnAssetModified(string modifiedAssetPath)
278 {
279 var isModifiedExternally = !(m_SelectedAssetPath != modifiedAssetPath || m_Model.isSaving);
280 if (!isModifiedExternally)
281 return;
282
283 SelectAsset(AssetDatabase.LoadAssetAtPath<SpriteLibraryAsset>(m_SelectedAssetPath), true);
284 }
285
286 void OnAssetMoved(string sourcePath, string destinationPath)
287 {
288 if (sourcePath == m_SelectedAssetPath)
289 {
290 if (string.IsNullOrEmpty(destinationPath))
291 {
292 SelectAsset(null, true);
293 }
294 else
295 {
296 m_Model.SetAssetPath(destinationPath);
297 m_SelectedAssetPath = destinationPath;
298 }
299 }
300 }
301
302 void SelectionChanged()
303 {
304 if (!m_SelectionLocked)
305 SelectAsset(SpriteLibrarySourceAssetImporter.GetAssetFromSelection());
306 }
307
308 void RefreshView()
309 {
310 var areCategoriesFiltered = m_FilterType is SearchType.CategoryAndLabel or SearchType.Category && !string.IsNullOrEmpty(m_FilterString);
311 var filteredCategories = m_Model.GetFilteredCategories(m_FilterString, m_FilterType);
312 m_ControllerEvents.onModifiedCategories?.Invoke(filteredCategories, areCategoriesFiltered);
313
314 var areLabelsFiltered = m_FilterType is SearchType.CategoryAndLabel or SearchType.Label && !string.IsNullOrEmpty(m_FilterString);
315 var filteredLabels = m_Model.GetFilteredLabels(m_FilterString, m_FilterType);
316 m_ControllerEvents.onModifiedLabels?.Invoke(filteredLabels, areLabelsFiltered);
317
318 m_ControllerEvents.onLibraryDataChanged?.Invoke(m_Model.isModified);
319 }
320
321 void RefreshSelection()
322 {
323 m_ControllerEvents.onSelectedCategories?.Invoke(selectedCategories);
324 m_ControllerEvents.onSelectedLabels?.Invoke(selectedLabels);
325 }
326
327 void PropagateLastAction()
328 {
329 if (SpriteLibraryEditorModel.IsActionModifyingAssets(m_Model.lastActionType))
330 AutoSave();
331
332 if (m_Model.lastActionType == ActionType.SetMainLibrary)
333 m_ControllerEvents.onMainLibraryChanged?.Invoke(m_Model.GetMainLibrary());
334
335 RefreshView();
336 RefreshSelection();
337
338 m_Model.lastActionType = ActionType.None;
339 }
340
341 void SetMainAsset(SpriteLibraryAsset libraryAsset)
342 {
343 if (!hasSelectedLibrary)
344 return;
345
346 var validAsset = true;
347 if (libraryAsset != null)
348 {
349 if (libraryAsset == m_Model.selectedAsset || SpriteLibrarySourceAssetImporter.GetAssetParentChain(libraryAsset).Contains(GetSelectedAsset()))
350 {
351 Debug.LogWarning(TextContent.spriteLibraryCircularDependency);
352 validAsset = false;
353 }
354 }
355
356 if (validAsset)
357 {
358 m_Model.BeginUndo(ActionType.SetMainLibrary, TextContent.spriteLibrarySetMainLibrary);
359 m_Model.SetMainLibrary(libraryAsset);
360 m_Model.SelectCategories(new List<string>());
361 m_Model.SelectLabels(new List<string>());
362 m_Model.EndUndo();
363
364 AutoSave();
365 m_ControllerEvents.onMainLibraryChanged?.Invoke(libraryAsset);
366 RefreshView();
367 RefreshSelection();
368 }
369 else
370 {
371 m_ControllerEvents.onMainLibraryChanged?.Invoke(m_Model.GetMainLibrary());
372 }
373 }
374
375 void SelectCategories(IList<string> newSelection)
376 {
377 if (!hasSelectedLibrary)
378 return;
379
380 newSelection ??= new List<string>();
381
382 if (Equals(m_Model.GetSelectedCategories(), newSelection))
383 return;
384
385 m_Model.BeginUndo(ActionType.SelectCategory, TextContent.spriteLibrarySelectCategories);
386 m_Model.SelectCategories(newSelection);
387 m_Model.SelectLabels(new List<string>());
388 m_Model.EndUndo();
389
390 RefreshView();
391 RefreshSelection();
392 }
393
394 void SelectLabels(IList<string> newSelection)
395 {
396 newSelection ??= new List<string>();
397
398 if (AreSequencesEqual(m_Model.GetSelectedLabels(), newSelection))
399 return;
400
401 m_Model.BeginUndo(ActionType.SelectLabels, TextContent.spriteLibrarySelectLabels);
402 m_Model.SelectLabels(newSelection);
403 m_Model.EndUndo();
404
405 RefreshSelection();
406 }
407
408 void CreateNewCategory(string categoryName = null, IList<Sprite> sprites = null)
409 {
410 if (!hasSelectedLibrary)
411 return;
412
413 if (string.IsNullOrEmpty(categoryName))
414 categoryName = k_DefaultCategoryName;
415
416 m_Model.BeginUndo(ActionType.CreateCategory, TextContent.spriteLibraryCreateCategory);
417 m_Model.CreateNewCategory(categoryName, sprites);
418 m_Model.SelectCategories(new List<string> { m_Model.GetAllCategories()[^1].name });
419 m_Model.SelectLabels(new List<string>());
420 m_Model.EndUndo();
421
422 AutoSave();
423 RefreshView();
424 RefreshSelection();
425 }
426
427 void RenameSelectedCategory(string newName)
428 {
429 if (!hasSelectedLibrary || !m_Model.hasSelectedCategories)
430 return;
431
432 newName = newName?.Trim();
433 if (string.IsNullOrEmpty(newName))
434 return;
435
436 var categoryData = GetCategoryData(selectedCategories[0]);
437 if (categoryData == null || categoryData.fromMain)
438 return;
439
440 m_Model.BeginUndo(ActionType.RenameCategory, TextContent.spriteLibraryRenameCategory);
441 m_Model.RenameSelectedCategory(newName);
442 m_Model.EndUndo();
443
444 AutoSave();
445 RefreshView();
446 RefreshSelection();
447 }
448
449 void ReorderCategories(IList<string> reorderedCategories)
450 {
451 if (!hasSelectedLibrary || !m_Model.hasSelectedCategories)
452 return;
453
454 if (m_Model.CompareCategories(reorderedCategories))
455 return;
456
457 var categoriesToReorder = selectedCategories;
458 m_Model.BeginUndo(ActionType.ReorderCategories, TextContent.spriteLibraryReorderCategories);
459 m_Model.ReorderCategories(reorderedCategories);
460 m_Model.SelectCategories(categoriesToReorder);
461 m_Model.EndUndo();
462
463 AutoSave();
464 RefreshView();
465 RefreshSelection();
466 }
467
468 void DeleteSelectedCategories()
469 {
470 if (!hasSelectedLibrary || !m_Model.hasSelectedCategories)
471 return;
472
473 var validCategories = false;
474 foreach (var category in selectedCategories)
475 {
476 var categoryData = GetCategoryData(category);
477 if (categoryData != null && !categoryData.fromMain)
478 {
479 validCategories = true;
480 break;
481 }
482 }
483
484 if (!validCategories)
485 return;
486
487 m_Model.BeginUndo(ActionType.DeleteCategories, TextContent.spriteLibraryDeleteCategories);
488 m_Model.DeleteSelectedCategories();
489 m_Model.SelectCategories(new List<string>());
490 m_Model.SelectLabels(new List<string>());
491 m_Model.EndUndo();
492
493 AutoSave();
494 RefreshView();
495 RefreshSelection();
496 }
497
498 void CreateNewLabel(string labelName = null)
499 {
500 if (!hasSelectedLibrary || !m_Model.hasSelectedCategories)
501 return;
502
503 if (string.IsNullOrEmpty(labelName))
504 labelName = k_DefaultLabelName;
505
506 m_Model.BeginUndo(ActionType.CreateLabel, TextContent.spriteLibraryCreateLabel);
507 m_Model.CreateNewLabel(labelName);
508 m_Model.SelectLabels(new List<string> { m_Model.GetAllLabels()[^1].name });
509 m_Model.EndUndo();
510
511 AutoSave();
512 RefreshView();
513 RefreshSelection();
514 }
515
516 void RenameSelectedLabel(string newName)
517 {
518 if (!hasSelectedLibrary || !m_Model.hasSelectedCategories || !m_Model.hasSelectedLabels)
519 return;
520
521 var labelData = GetLabelData(selectedLabels[0]);
522 if (labelData == null || labelData.fromMain)
523 return;
524
525 newName = newName?.Trim();
526 if (newName == string.Empty)
527 return;
528
529 m_Model.BeginUndo(ActionType.RenameLabel, TextContent.spriteLibraryRenameLabel);
530 m_Model.RenameSelectedLabel(newName);
531 m_Model.EndUndo();
532
533 AutoSave();
534 RefreshView();
535 RefreshSelection();
536 }
537
538 void ReorderLabels(IList<string> reorderedLabels)
539 {
540 if (!hasSelectedLibrary || !m_Model.hasSelectedCategories)
541 return;
542
543 if (m_Model.CompareLabels(reorderedLabels))
544 return;
545
546 var labelsToReorder = selectedLabels;
547 m_Model.BeginUndo(ActionType.ReorderLabels, TextContent.spriteLibraryReorderLabels);
548 m_Model.ReorderLabels(reorderedLabels);
549 m_Model.SelectLabels(labelsToReorder);
550 m_Model.EndUndo();
551
552 AutoSave();
553 RefreshView();
554 }
555
556 void DeleteSelectedLabels()
557 {
558 if (!hasSelectedLibrary || !m_Model.hasSelectedCategories || !m_Model.hasSelectedLabels)
559 return;
560
561 var canAnyLabelBeDeleted = false;
562 foreach (var label in selectedLabels)
563 {
564 if (!GetLabelData(label).fromMain)
565 {
566 canAnyLabelBeDeleted = true;
567 break;
568 }
569 }
570
571 if (!canAnyLabelBeDeleted)
572 return;
573
574 m_Model.BeginUndo(ActionType.DeleteLabels, TextContent.spriteLibraryDeleteLabels);
575 m_Model.DeleteSelectedLabels();
576 m_Model.SelectLabels(new List<string>());
577 m_Model.EndUndo();
578
579 AutoSave();
580 RefreshView();
581 RefreshSelection();
582 }
583
584 void SetLabelSprite(string labelName, Sprite newSprite)
585 {
586 if (!hasSelectedLibrary || !m_Model.hasSelectedCategories)
587 return;
588
589 var labelData = GetLabelData(labelName);
590 if (labelData == null || labelData.sprite == newSprite)
591 return;
592
593 m_Model.BeginUndo(ActionType.SetLabelSprite, TextContent.spriteLibrarySetLabelSprite);
594 m_Model.SetLabelSprite(labelName, newSprite);
595 m_Model.EndUndo();
596
597 AutoSave();
598 RefreshView();
599 RefreshSelection();
600 }
601
602 void ToggleAutoSave(bool newAutoSaveValue)
603 {
604 m_AutoSave = newAutoSaveValue;
605 if (m_AutoSave)
606 OnSave();
607 }
608
609 void OnSave()
610 {
611 m_Window.SaveChanges();
612 }
613
614 void OnRevert()
615 {
616 if (m_Window.hasUnsavedChanges)
617 m_Window.HandleRevertChanges();
618 }
619
620 void AutoSave()
621 {
622 if (m_AutoSave)
623 m_Window.SaveChanges();
624 }
625
626 void ToggleSelectionLock(bool isSelectionLocked)
627 {
628 m_SelectionLocked = isSelectionLocked;
629
630 SelectionChanged();
631 }
632
633 void AddDataToCategories(IList<DragAndDropData> spritesData, bool alt, string category)
634 {
635 if (!hasSelectedLibrary)
636 return;
637
638 if (spritesData == null || spritesData.Count == 0)
639 return;
640
641 m_Model.BeginUndo(ActionType.ModifiedCategories, TextContent.spriteLibraryAddDataToCategories);
642
643 foreach (var data in spritesData)
644 {
645 var isDroppedIntoEmptyArea = string.IsNullOrEmpty(category);
646 if (isDroppedIntoEmptyArea)
647 {
648 if (data.spriteSourceType == SpriteSourceType.Psb)
649 HandlePsdData(data);
650 else if (data.spriteSourceType == SpriteSourceType.Sprite)
651 m_Model.CreateNewCategory(data.name, data.sprites);
652 }
653 else
654 m_Model.AddLabelsToCategory(category, data.sprites, true);
655 }
656
657 m_Model.EndUndo();
658
659 AutoSave();
660 RefreshView();
661 RefreshSelection();
662 }
663
664 void AddDataToLabels(IList<DragAndDropData> spritesData, bool alt, string label)
665 {
666 if (!hasSelectedLibrary || !m_Model.hasSelectedCategories)
667 return;
668
669 var sprites = new List<Sprite>();
670 foreach (var data in spritesData)
671 sprites.AddRange(data.sprites);
672
673 if (sprites.Count == 0)
674 return;
675
676 m_Model.BeginUndo(ActionType.ModifiedLabels, TextContent.spriteLibraryAddDataToLabels);
677 if (!string.IsNullOrEmpty(label))
678 m_Model.SetLabelSprite(label, sprites[0]);
679 else // empty area
680 m_Model.AddLabelsToCategory(selectedCategories[0], sprites, false);
681 m_Model.EndUndo();
682
683 AutoSave();
684 RefreshView();
685 RefreshSelection();
686 }
687
688 void HandlePsdData(DragAndDropData data)
689 {
690 var psdFilePath = AssetDatabase.GetAssetPath(data.sprites[0]);
691 var characterObj = AssetDatabase.LoadAssetAtPath<GameObject>(psdFilePath);
692 var categoryDictionary = new Dictionary<string, List<Sprite>>();
693 var objectList = new Queue<Transform>();
694 objectList.Enqueue(characterObj.transform);
695 while (objectList.Count > 0)
696 {
697 var goTransform = objectList.Dequeue();
698 var spriteList = new List<Sprite>();
699 for (var i = 0; i < goTransform.childCount; i++)
700 {
701 var childTransform = goTransform.GetChild(i);
702
703 var spriteRenderer = childTransform.GetComponent<SpriteRenderer>();
704 if (spriteRenderer != null)
705 spriteList.Add(spriteRenderer.sprite);
706 else
707 objectList.Enqueue(childTransform);
708 }
709
710 if (spriteList.Count > 0)
711 {
712 if (goTransform == characterObj.transform)
713 {
714 foreach (var sprite in spriteList)
715 categoryDictionary[sprite.name] = new List<Sprite> { sprite };
716 }
717 else
718 {
719 categoryDictionary[goTransform.name] = spriteList;
720 }
721 }
722 }
723
724 foreach (var (categoryName, sprites) in categoryDictionary)
725 {
726 var addedToCategory = false;
727 foreach (var cat in m_Model.GetAllCategories())
728 {
729 if (cat.name == categoryName)
730 {
731 m_Model.AddLabelsToCategory(categoryName, sprites, true);
732 addedToCategory = true;
733 break;
734 }
735 }
736
737 if (!addedToCategory)
738 m_Model.CreateNewCategory(categoryName, sprites);
739 }
740 }
741
742 void RevertOverridenLabels(IList<string> labels)
743 {
744 if (!hasSelectedLibrary || !m_Model.hasSelectedCategories)
745 return;
746
747 var canRevertChanges = false;
748 foreach (var label in labels)
749 {
750 if (string.IsNullOrEmpty(label))
751 continue;
752
753 var data = GetLabelData(label);
754 if (data != null && (data.spriteOverride || data.categoryFromMain && !data.fromMain))
755 {
756 canRevertChanges = true;
757 break;
758 }
759 }
760
761 if (!canRevertChanges)
762 return;
763
764 m_Model.BeginUndo(ActionType.SetLabelSprite, TextContent.spriteLibrarySetLabelSprite);
765 m_Model.RevertLabels(labels);
766 m_Model.SelectLabels(new List<string>());
767 m_Model.EndUndo();
768
769 AutoSave();
770 RefreshView();
771 RefreshSelection();
772 }
773
774 CategoryData GetCategoryData(string categoryName)
775 {
776 if (string.IsNullOrEmpty(categoryName))
777 return null;
778
779 foreach (var category in m_Model.GetAllCategories())
780 {
781 if (category.name == categoryName)
782 return category;
783 }
784
785 return null;
786 }
787
788 LabelData GetLabelData(string labelName)
789 {
790 if (string.IsNullOrEmpty(labelName))
791 return null;
792
793 foreach (var label in m_Model.GetAllLabels())
794 {
795 if (label.name == labelName)
796 return label;
797 }
798
799 return null;
800 }
801
802 static bool AreSequencesEqual(IList<string> first, IList<string> second)
803 {
804 if (first == null || second == null || first.Count != second.Count)
805 return false;
806
807 for (var i = 0; i < first.Count; i++)
808 {
809 if (first[i] != second[i])
810 return false;
811 }
812
813 return true;
814 }
815 }
816}