258 lines
9.3 KiB
C#
258 lines
9.3 KiB
C#
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using TMPro;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using UnityEngine.TextCore;
|
||
|
||
namespace BrewMonster.Scripts.Editor
|
||
{
|
||
/// <summary>
|
||
/// Bulk chỉnh BX/BY/AD/Scale trên TMP_SpriteAsset (spriteGlyphTable).
|
||
/// Bulk-edit BX/BY/AD/Scale on TMP_SpriteAsset (spriteGlyphTable).
|
||
/// </summary>
|
||
public sealed class TmpSpriteAssetGlyphMetricsBulkWindow : EditorWindow
|
||
{
|
||
private enum ValueMode
|
||
{
|
||
Absolute,
|
||
AddDelta
|
||
}
|
||
|
||
private readonly List<TMP_SpriteAsset> _targets = new List<TMP_SpriteAsset>();
|
||
private Vector2 _scroll;
|
||
private string _nameContains = "";
|
||
|
||
private ValueMode _mode = ValueMode.Absolute;
|
||
|
||
private bool _setBx = true;
|
||
private bool _setBy = true;
|
||
private bool _setAd = true;
|
||
private bool _setScale;
|
||
|
||
private float _bx;
|
||
private float _by = 10f;
|
||
private float _ad = 32f;
|
||
private float _scale = 1f;
|
||
|
||
[MenuItem("Tools/Perfect World/ChatSystem/TMP Sprite Metrics (BX/BY/AD)…")]
|
||
public static void Open()
|
||
{
|
||
var w = GetWindow<TmpSpriteAssetGlyphMetricsBulkWindow>(false, "TMP Sprite Metrics", true);
|
||
w.minSize = new Vector2(420f, 360f);
|
||
}
|
||
|
||
private void OnGUI()
|
||
{
|
||
EditorGUILayout.Space(4f);
|
||
EditorGUILayout.LabelField("TMP_SpriteAsset — BX / BY / AD / Scale", EditorStyles.boldLabel);
|
||
EditorGUILayout.HelpBox(
|
||
"Áp dụng lên bảng glyph (spriteGlyphTable). Lọc theo tên sprite (cột Name) nếu cần.\n" +
|
||
"Applies to spriteGlyphTable. Optional name filter uses sprite character names.",
|
||
MessageType.None);
|
||
|
||
EditorGUILayout.Space(6f);
|
||
DrawTargetsSection();
|
||
|
||
EditorGUILayout.Space(8f);
|
||
_nameContains = EditorGUILayout.TextField("Name contains (optional)", _nameContains);
|
||
|
||
_mode = (ValueMode)EditorGUILayout.EnumPopup("Mode", _mode);
|
||
|
||
EditorGUILayout.Space(4f);
|
||
EditorGUILayout.LabelField("Fields to write", EditorStyles.boldLabel);
|
||
EditorGUILayout.BeginHorizontal();
|
||
_setBx = GUILayout.Toggle(_setBx, "BX", GUILayout.Width(44));
|
||
_setBy = GUILayout.Toggle(_setBy, "BY", GUILayout.Width(44));
|
||
_setAd = GUILayout.Toggle(_setAd, "AD", GUILayout.Width(44));
|
||
_setScale = GUILayout.Toggle(_setScale, "Scale", GUILayout.Width(60));
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
string hint = _mode == ValueMode.Absolute
|
||
? "Giá trị gán thẳng vào metrics / Set metrics to these values."
|
||
: "Cộng dồn lên giá trị hiện tại / Add to current values.";
|
||
EditorGUILayout.HelpBox(hint, MessageType.None);
|
||
|
||
using (new EditorGUI.DisabledScope(!_setBx))
|
||
_bx = EditorGUILayout.FloatField("BX (bearing X)", _bx);
|
||
using (new EditorGUI.DisabledScope(!_setBy))
|
||
_by = EditorGUILayout.FloatField("BY (bearing Y)", _by);
|
||
using (new EditorGUI.DisabledScope(!_setAd))
|
||
_ad = EditorGUILayout.FloatField("AD (advance)", _ad);
|
||
using (new EditorGUI.DisabledScope(!_setScale))
|
||
_scale = EditorGUILayout.FloatField("Scale", _scale);
|
||
|
||
EditorGUILayout.Space(10f);
|
||
using (new EditorGUI.DisabledScope(_targets.Count == 0 || (!_setBx && !_setBy && !_setAd && !_setScale)))
|
||
{
|
||
if (GUILayout.Button("Apply to assets", GUILayout.Height(32)))
|
||
ApplyAll();
|
||
}
|
||
|
||
EditorGUILayout.Space(6f);
|
||
DrawSummary();
|
||
}
|
||
|
||
private void DrawTargetsSection()
|
||
{
|
||
EditorGUILayout.LabelField("Sprite assets", EditorStyles.boldLabel);
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
if (GUILayout.Button("Use selection (Project)", GUILayout.Height(22)))
|
||
AddFromSelection();
|
||
if (GUILayout.Button("Clear list", GUILayout.Width(100)))
|
||
_targets.Clear();
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
_scroll = EditorGUILayout.BeginScrollView(_scroll, GUILayout.MinHeight(120f));
|
||
for (int i = 0; i < _targets.Count; i++)
|
||
{
|
||
EditorGUILayout.BeginHorizontal();
|
||
_targets[i] = (TMP_SpriteAsset)EditorGUILayout.ObjectField(
|
||
_targets[i], typeof(TMP_SpriteAsset), false);
|
||
if (GUILayout.Button("−", GUILayout.Width(24)))
|
||
{
|
||
_targets.RemoveAt(i);
|
||
i--;
|
||
}
|
||
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
|
||
EditorGUILayout.EndScrollView();
|
||
|
||
var drop = (TMP_SpriteAsset)EditorGUILayout.ObjectField(
|
||
"Drag asset here to add", null, typeof(TMP_SpriteAsset), false);
|
||
if (drop != null && !_targets.Contains(drop))
|
||
_targets.Add(drop);
|
||
}
|
||
|
||
private void AddFromSelection()
|
||
{
|
||
foreach (Object o in Selection.objects)
|
||
{
|
||
if (o is TMP_SpriteAsset sa && !_targets.Contains(sa))
|
||
_targets.Add(sa);
|
||
}
|
||
}
|
||
|
||
private void DrawSummary()
|
||
{
|
||
int n = _targets.Count;
|
||
if (n == 0)
|
||
{
|
||
EditorGUILayout.LabelField("No assets in list.");
|
||
return;
|
||
}
|
||
|
||
int glyphs = _targets.Where(t => t != null).Sum(t => t.spriteGlyphTable?.Count ?? 0);
|
||
EditorGUILayout.LabelField($"Assets: {n} | Total glyphs: {glyphs}");
|
||
}
|
||
|
||
private void ApplyAll()
|
||
{
|
||
if (!_setBx && !_setBy && !_setAd && !_setScale)
|
||
{
|
||
EditorUtility.DisplayDialog("TMP Sprite Metrics", "Chọn ít nhất một field (BX/BY/AD/Scale).", "OK");
|
||
return;
|
||
}
|
||
|
||
int changedAssets = 0;
|
||
int changedGlyphs = 0;
|
||
|
||
foreach (TMP_SpriteAsset asset in _targets.Where(a => a != null))
|
||
{
|
||
string path = AssetDatabase.GetAssetPath(asset);
|
||
if (string.IsNullOrEmpty(path))
|
||
continue;
|
||
|
||
Undo.RegisterCompleteObjectUndo(asset, "TMP Sprite glyph metrics");
|
||
|
||
var glyphTable = asset.spriteGlyphTable;
|
||
if (glyphTable == null || glyphTable.Count == 0)
|
||
continue;
|
||
|
||
HashSet<uint> allowed = BuildGlyphIndexFilter(asset);
|
||
bool touched = false;
|
||
|
||
foreach (TMP_SpriteGlyph g in glyphTable)
|
||
{
|
||
if (g == null)
|
||
continue;
|
||
if (allowed != null && !allowed.Contains(g.index))
|
||
continue;
|
||
|
||
GlyphMetrics m = g.metrics;
|
||
|
||
if (_setBx)
|
||
m.horizontalBearingX = _mode == ValueMode.Absolute ? _bx : m.horizontalBearingX + _bx;
|
||
if (_setBy)
|
||
m.horizontalBearingY = _mode == ValueMode.Absolute ? _by : m.horizontalBearingY + _by;
|
||
if (_setAd)
|
||
m.horizontalAdvance = _mode == ValueMode.Absolute ? _ad : m.horizontalAdvance + _ad;
|
||
|
||
g.metrics = m;
|
||
|
||
if (_setScale)
|
||
g.scale = _mode == ValueMode.Absolute ? _scale : g.scale + _scale;
|
||
|
||
touched = true;
|
||
changedGlyphs++;
|
||
}
|
||
|
||
if (_setScale)
|
||
{
|
||
var chars = asset.spriteCharacterTable;
|
||
if (chars != null)
|
||
{
|
||
foreach (TMP_SpriteCharacter c in chars)
|
||
{
|
||
if (c == null)
|
||
continue;
|
||
if (allowed != null && !allowed.Contains(c.glyphIndex))
|
||
continue;
|
||
c.scale = _mode == ValueMode.Absolute ? _scale : c.scale + _scale;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (touched)
|
||
{
|
||
asset.UpdateLookupTables();
|
||
EditorUtility.SetDirty(asset);
|
||
changedAssets++;
|
||
}
|
||
}
|
||
|
||
AssetDatabase.SaveAssets();
|
||
Debug.Log($"[Cuong] TMP Sprite Metrics: updated {changedGlyphs} glyph(s) across {changedAssets} asset(s).");
|
||
}
|
||
|
||
/// <summary>
|
||
/// null = không lọc / no filter
|
||
/// </summary>
|
||
private HashSet<uint> BuildGlyphIndexFilter(TMP_SpriteAsset asset)
|
||
{
|
||
string f = _nameContains?.Trim();
|
||
if (string.IsNullOrEmpty(f))
|
||
return null;
|
||
|
||
var set = new HashSet<uint>();
|
||
var chars = asset.spriteCharacterTable;
|
||
if (chars == null)
|
||
return set;
|
||
|
||
foreach (TMP_SpriteCharacter c in chars)
|
||
{
|
||
if (c == null || string.IsNullOrEmpty(c.name))
|
||
continue;
|
||
if (c.name.IndexOf(f, System.StringComparison.OrdinalIgnoreCase) < 0)
|
||
continue;
|
||
set.Add(c.glyphIndex);
|
||
}
|
||
|
||
return set;
|
||
}
|
||
}
|
||
}
|