// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // #if UNITY_EDITOR using System; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEditorInternal; using UnityEngine; namespace Animancer.Editor.Tools { /// [Editor-Only] [Pro-Only] A for bulk-renaming s. /// /// Documentation: /// /// Rename Sprites /// /// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/RenameSpritesTool /// [Serializable] public class RenameSpritesTool : SpriteModifierTool { /************************************************************************************************************************/ [NonSerialized] private List _GeneratedNames; [NonSerialized] private bool _NamesAreDirty; [NonSerialized] private ReorderableList _SpritesDisplay; [SerializeField] private List _ManualNames; [SerializeField] private int _FirstIndex = 1; [SerializeField] private int _MinimumDigits = 1; /************************************************************************************************************************/ /// public override int DisplayOrder => 2; /// public override string Name => "Rename Sprites"; /// public override string HelpURL => Strings.DocsURLs.RenameSprites; /// public override string Instructions { get { if (Sprites.Count == 0) return "Select the Sprites you want to rename."; return "Enter the new name(s) you want to give the Sprites then click Apply." + "\n\nEach Sprite below the name you enter will be given the same name" + " until the next name which will restart the counter."; } } /************************************************************************************************************************/ /// public override void OnEnable(int index) { base.OnEnable(index); _ManualNames ??= new(); _GeneratedNames ??= new(); _SpritesDisplay = AnimancerToolsWindow.CreateReorderableObjectList(Sprites, "Sprites to Rename"); _SpritesDisplay.onChangedCallback += list => DirtyNames(); _SpritesDisplay.drawElementCallback = DrawItem; _SpritesDisplay.elementHeight = AnimancerGUI.LineHeight * 3 + AnimancerGUI.StandardSpacing * 2; } /************************************************************************************************************************/ /// public override void OnSelectionChanged() { base.OnSelectionChanged(); DirtyNames(); } /************************************************************************************************************************/ /// Refreshes the . private void UpdateNames() { if (!_NamesAreDirty) return; _NamesAreDirty = false; var sprites = Sprites; AnimancerEditorUtilities.SetCount(_ManualNames, sprites.Count); AnimancerEditorUtilities.SetCount(_GeneratedNames, sprites.Count); string name = null; string digitFormat = null; int index = 0; for (int i = 0; i < sprites.Count; i++) { var newName = _ManualNames[i]; if (!string.IsNullOrWhiteSpace(newName)) { name = newName; index = 0; var nextNameIndex = IndexOfNextManualName(i); var digits = Mathf.FloorToInt(Mathf.Log10(nextNameIndex - i)) + 1; if (digits < _MinimumDigits) digits = _MinimumDigits; var formatCharacters = new char[digits]; for (int iDigit = 0; iDigit < digits; iDigit++) formatCharacters[iDigit] = '0'; digitFormat = new string(formatCharacters); } _GeneratedNames[i] = string.IsNullOrWhiteSpace(name) ? sprites[i].name : name + (index + _FirstIndex).ToString(digitFormat); index++; } } /************************************************************************************************************************/ private int IndexOfNextManualName(int startIndex) { for (int i = startIndex + 1; i < _ManualNames.Count; i++) if (!string.IsNullOrWhiteSpace(_ManualNames[i])) return i; return _ManualNames.Count; } /************************************************************************************************************************/ /// public override void DoBodyGUI() { base.DoBodyGUI(); #if ! UNITY_2D_SPRITE EditorGUILayout.HelpBox( "Without the 2D Sprite Package," + " any references to the renamed sprites will be lost (including animations).", MessageType.Warning); #endif AnimancerToolsWindow.BeginChangeCheck(); var firstIndex = EditorGUILayout.IntField("First Index", _FirstIndex); if (AnimancerToolsWindow.EndChangeCheck(ref _FirstIndex, Mathf.Max(firstIndex, 0))) DirtyNames(); AnimancerToolsWindow.BeginChangeCheck(); var digits = EditorGUILayout.IntField("Minimum Digits", _MinimumDigits); if (AnimancerToolsWindow.EndChangeCheck(ref _MinimumDigits, Mathf.Max(digits, 1))) DirtyNames(); UpdateNames(); _SpritesDisplay.DoLayoutList(); GUILayout.BeginHorizontal(); { GUILayout.FlexibleSpace(); GUI.enabled = HasAnyNames(); if (GUILayout.Button("Clear")) { AnimancerGUI.Deselect(); AnimancerToolsWindow.RecordUndo(); _ManualNames.Clear(); DirtyNames(); } GUI.enabled = HasAnyDifferentNames(); if (GUILayout.Button("Apply")) { AnimancerGUI.Deselect(); AskAndApply(); } } GUILayout.EndHorizontal(); } /************************************************************************************************************************/ private void DrawItem(Rect area, int index, bool isActive, bool isFocused) { var sprites = Sprites; var sprite = sprites[index]; var thumbnailWidth = Math.Min(area.height, area.width * 0.5f); var thumbnailArea = AnimancerGUI.StealFromLeft(ref area, thumbnailWidth, AnimancerGUI.StandardSpacing); AnimancerGUI.DrawSprite(thumbnailArea, sprite); area.y += (AnimancerGUI.LineHeight + AnimancerGUI.StandardSpacing) * 0.5f; area.height = AnimancerGUI.LineHeight; sprites[index] = DrawSpriteField(area, sprite); AnimancerGUI.NextVerticalArea(ref area); DrawName(area, index); } /************************************************************************************************************************/ private Sprite DrawSpriteField(Rect area, Sprite sprite) => AnimancerGUI.DoObjectFieldGUI(area, "", sprite, false); /************************************************************************************************************************/ private static GUIStyle _TextFieldStyle; private void DrawName(Rect area, int index) { area.y += 1; area.height = AnimancerGUI.LineHeight; var manualName = _ManualNames[index]; var generatedName = _GeneratedNames[index]; if (Event.current.type == EventType.Repaint) { _TextFieldStyle ??= new(EditorStyles.textField); _TextFieldStyle.fontStyle = string.IsNullOrWhiteSpace(manualName) ? FontStyle.Italic : FontStyle.Bold; GUI.TextField(area, generatedName, _TextFieldStyle); } else { EditorGUI.BeginChangeCheck(); _ManualNames[index] = GUI.TextField(area, manualName); if (EditorGUI.EndChangeCheck()) DirtyNames(); } } /************************************************************************************************************************/ private bool HasAnyNames() { var sprites = Sprites; for (int i = 0; i < sprites.Count; i++) if (!string.IsNullOrWhiteSpace(_ManualNames[i])) return true; return false; } private bool HasAnyDifferentNames() { var sprites = Sprites; for (int i = 0; i < sprites.Count; i++) if (sprites[i].name != _GeneratedNames[i]) return true; return false; } /************************************************************************************************************************/ private void DirtyNames() => _NamesAreDirty = true; /************************************************************************************************************************/ /// protected override string AreYouSure => "Are you sure you want to rename these Sprites?" #if UNITY_2D_SPRITE ; #else + "\n\nAny references to the renamed Sprites will be lost (including animations that use them)." + " This can be avoided by importing Unity's 2D Sprite Package before using this tool."; #endif /************************************************************************************************************************/ private static Dictionary _SpriteToName; /// protected override void BeforeApply() { if (_SpriteToName == null) _SpriteToName = new(); else _SpriteToName.Clear(); var sprites = Sprites; for (int i = 0; i < sprites.Count; i++) { _SpriteToName.Add(sprites[i], _GeneratedNames[i]); } // Renaming selected Sprites will lose the selection without triggering OnSelectionChanged. EditorApplication.delayCall += OnSelectionChanged; } /************************************************************************************************************************/ /// protected override void Modify(SpriteDataEditor data, int index, Sprite sprite) { data.SetName(index, _SpriteToName[sprite]); } /************************************************************************************************************************/ /// protected override void Modify(TextureImporter importer, List sprites) { if (sprites.Count == 1 && importer.spriteImportMode != SpriteImportMode.Multiple) { var sprite = sprites[0]; var fileName = Path.GetFileNameWithoutExtension(importer.assetPath); if (fileName == sprite.name) { AssetDatabase.RenameAsset(importer.assetPath, _SpriteToName[sprite]); sprites.Clear(); } } base.Modify(importer, sprites); } /************************************************************************************************************************/ /// protected override void AfterApply() { base.AfterApply(); AnimancerToolsWindow.RecordUndo(); _ManualNames.Clear(); } /************************************************************************************************************************/ } } #endif