RenameSpritesTool.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. #if UNITY_EDITOR
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using UnityEditor;
  7. using UnityEditorInternal;
  8. using UnityEngine;
  9. namespace Animancer.Editor.Tools
  10. {
  11. /// <summary>[Editor-Only] [Pro-Only] A <see cref="SpriteModifierTool"/> for bulk-renaming <see cref="Sprite"/>s.</summary>
  12. /// <remarks>
  13. /// Documentation: <see href="https://kybernetik.com.au/animancer/docs/manual/tools/rename-sprites">Rename Sprites</see>
  14. /// </remarks>
  15. /// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/RenameSpritesTool
  16. ///
  17. [Serializable]
  18. public class RenameSpritesTool : SpriteModifierTool
  19. {
  20. /************************************************************************************************************************/
  21. [NonSerialized] private List<string> _Names;
  22. [NonSerialized] private bool _NamesAreDirty;
  23. [NonSerialized] private ReorderableList _SpritesDisplay;
  24. [NonSerialized] private ReorderableList _NamesDisplay;
  25. [SerializeField] private string _NewName = "";
  26. [SerializeField] private int _MinimumDigits;
  27. /************************************************************************************************************************/
  28. /// <inheritdoc/>
  29. public override int DisplayOrder => 2;
  30. /// <inheritdoc/>
  31. public override string Name => "Rename Sprites";
  32. /// <inheritdoc/>
  33. public override string HelpURL => Strings.DocsURLs.RenameSprites;
  34. /// <inheritdoc/>
  35. public override string Instructions
  36. {
  37. get
  38. {
  39. if (Sprites.Count == 0)
  40. return "Select the Sprites you want to rename.";
  41. return "Enter the new name(s) you want to give the Sprites then click Apply.";
  42. }
  43. }
  44. /************************************************************************************************************************/
  45. /// <inheritdoc/>
  46. public override void OnEnable(int index)
  47. {
  48. base.OnEnable(index);
  49. _Names = new List<string>();
  50. _SpritesDisplay = AnimancerToolsWindow.CreateReorderableObjectList(Sprites, "Sprites");
  51. _NamesDisplay = AnimancerToolsWindow.CreateReorderableStringList(_Names, "Names");
  52. }
  53. /************************************************************************************************************************/
  54. /// <inheritdoc/>
  55. public override void OnSelectionChanged()
  56. {
  57. base.OnSelectionChanged();
  58. _NamesAreDirty = true;
  59. }
  60. /************************************************************************************************************************/
  61. /// <summary>Refreshes the <see cref="_Names"/>.</summary>
  62. private void UpdateNames()
  63. {
  64. if (!_NamesAreDirty)
  65. return;
  66. _NamesAreDirty = false;
  67. var sprites = Sprites;
  68. AnimancerEditorUtilities.SetCount(_Names, sprites.Count);
  69. if (string.IsNullOrEmpty(_NewName))
  70. {
  71. for (int i = 0; i < sprites.Count; i++)
  72. _Names[i] = sprites[i].name;
  73. }
  74. else
  75. {
  76. var digits = Mathf.FloorToInt(Mathf.Log10(_Names.Count)) + 1;
  77. if (digits < _MinimumDigits)
  78. digits = _MinimumDigits;
  79. var formatCharacters = new char[digits];
  80. for (int i = 0; i < digits; i++)
  81. formatCharacters[i] = '0';
  82. var format = new string(formatCharacters);
  83. for (int i = 0; i < _Names.Count; i++)
  84. _Names[i] = _NewName + (i + 1).ToString(format);
  85. }
  86. }
  87. /************************************************************************************************************************/
  88. /// <inheritdoc/>
  89. public override void DoBodyGUI()
  90. {
  91. base.DoBodyGUI();
  92. EditorGUILayout.HelpBox(ReferencesLostMessage, MessageType.Warning);
  93. AnimancerToolsWindow.BeginChangeCheck();
  94. var newName = EditorGUILayout.TextField("New Name", _NewName);
  95. if (AnimancerToolsWindow.EndChangeCheck(ref _NewName, newName))
  96. _NamesAreDirty = true;
  97. AnimancerToolsWindow.BeginChangeCheck();
  98. var digits = EditorGUILayout.IntField("Minimum Digits", _MinimumDigits);
  99. if (AnimancerToolsWindow.EndChangeCheck(ref _MinimumDigits, Mathf.Max(digits, 1)))
  100. _NamesAreDirty = true;
  101. UpdateNames();
  102. GUILayout.BeginHorizontal();
  103. {
  104. GUILayout.BeginVertical();
  105. _SpritesDisplay.DoLayoutList();
  106. GUILayout.EndVertical();
  107. GUILayout.BeginVertical();
  108. _NamesDisplay.DoLayoutList();
  109. GUILayout.EndVertical();
  110. }
  111. GUILayout.EndHorizontal();
  112. GUILayout.BeginHorizontal();
  113. {
  114. GUILayout.FlexibleSpace();
  115. GUI.enabled = _NewName.Length > 0;
  116. if (GUILayout.Button("Clear"))
  117. {
  118. AnimancerGUI.Deselect();
  119. AnimancerToolsWindow.RecordUndo();
  120. _NewName = "";
  121. _NamesAreDirty = true;
  122. }
  123. GUI.enabled = _SpritesDisplay.list.Count > 0;
  124. if (GUILayout.Button("Apply"))
  125. {
  126. AnimancerGUI.Deselect();
  127. AskAndApply();
  128. }
  129. }
  130. GUILayout.EndHorizontal();
  131. }
  132. /************************************************************************************************************************/
  133. // We could prevent it from causing animations to lose their data by using ISpriteEditorDataProvider
  134. // instead of TextureImporter, but it's in the 2D Sprite package which Animancer does not otherwise require.
  135. private const string ReferencesLostMessage =
  136. "Any references to the renamed Sprites will be lost (including animations that use them)" +
  137. " but you can use the 'Remap Sprite Animations' tool to reassign them afterwards.";
  138. /************************************************************************************************************************/
  139. /// <inheritdoc/>
  140. protected override string AreYouSure =>
  141. "Are you sure you want to rename these Sprites?" +
  142. "\n\n" + ReferencesLostMessage;
  143. /************************************************************************************************************************/
  144. private static Dictionary<Sprite, string> _SpriteToName;
  145. /// <inheritdoc/>
  146. protected override void PrepareToApply()
  147. {
  148. if (_SpriteToName == null)
  149. _SpriteToName = new Dictionary<Sprite, string>();
  150. else
  151. _SpriteToName.Clear();
  152. var sprites = Sprites;
  153. for (int i = 0; i < sprites.Count; i++)
  154. {
  155. _SpriteToName.Add(sprites[i], _Names[i]);
  156. }
  157. // Renaming selected Sprites will lose the selection without triggering OnSelectionChanged.
  158. EditorApplication.delayCall += OnSelectionChanged;
  159. }
  160. /************************************************************************************************************************/
  161. /// <inheritdoc/>
  162. protected override void Modify(SpriteDataEditor data, int index, Sprite sprite)
  163. {
  164. data.SetName(index, _SpriteToName[sprite]);
  165. }
  166. /************************************************************************************************************************/
  167. /// <inheritdoc/>
  168. protected override void Modify(TextureImporter importer, List<Sprite> sprites)
  169. {
  170. if (sprites.Count == 1 && importer.spriteImportMode != SpriteImportMode.Multiple)
  171. {
  172. var sprite = sprites[0];
  173. var fileName = Path.GetFileNameWithoutExtension(importer.assetPath);
  174. if (fileName == sprite.name)
  175. {
  176. AssetDatabase.RenameAsset(importer.assetPath, _SpriteToName[sprite]);
  177. sprites.Clear();
  178. }
  179. }
  180. base.Modify(importer, sprites);
  181. }
  182. /************************************************************************************************************************/
  183. }
  184. }
  185. #endif