chore: initial commit
This commit is contained in:
@@ -0,0 +1,359 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor.Tools
|
||||
{
|
||||
/// <summary>[Editor-Only] [Pro-Only] A <see cref="SpriteModifierTool"/> for bulk-renaming <see cref="Sprite"/>s.</summary>
|
||||
/// <remarks>
|
||||
/// <strong>Documentation:</strong>
|
||||
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools/rename-sprites">
|
||||
/// Rename Sprites</see>
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/RenameSpritesTool
|
||||
///
|
||||
[Serializable]
|
||||
public class RenameSpritesTool : SpriteModifierTool
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[NonSerialized] private List<string> _GeneratedNames;
|
||||
[NonSerialized] private bool _NamesAreDirty;
|
||||
[NonSerialized] private ReorderableList _SpritesDisplay;
|
||||
|
||||
[SerializeField] private List<string> _ManualNames;
|
||||
[SerializeField] private int _FirstIndex = 1;
|
||||
[SerializeField] private int _MinimumDigits = 1;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int DisplayOrder => 2;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Name => "Rename Sprites";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string HelpURL => Strings.DocsURLs.RenameSprites;
|
||||
|
||||
/// <inheritdoc/>
|
||||
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.";
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnSelectionChanged()
|
||||
{
|
||||
base.OnSelectionChanged();
|
||||
DirtyNames();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Refreshes the <see cref="_GeneratedNames"/>.</summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
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<Sprite, string> _SpriteToName;
|
||||
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Modify(SpriteDataEditor data, int index, Sprite sprite)
|
||||
{
|
||||
data.SetName(index, _SpriteToName[sprite]);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Modify(TextureImporter importer, List<Sprite> 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);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void AfterApply()
|
||||
{
|
||||
base.AfterApply();
|
||||
|
||||
AnimancerToolsWindow.RecordUndo();
|
||||
_ManualNames.Clear();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user