using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEditor;
using UnityEngine;
using UnityEngine.TextCore;
namespace BrewMonster.Scripts.Editor
{
///
/// Bulk chỉnh BX/BY/AD/Scale trên TMP_SpriteAsset (spriteGlyphTable).
/// Bulk-edit BX/BY/AD/Scale on TMP_SpriteAsset (spriteGlyphTable).
///
public sealed class TmpSpriteAssetGlyphMetricsBulkWindow : EditorWindow
{
private enum ValueMode
{
Absolute,
AddDelta
}
private readonly List _targets = new List();
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(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 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).");
}
///
/// null = không lọc / no filter
///
private HashSet BuildGlyphIndexFilter(TMP_SpriteAsset asset)
{
string f = _nameContains?.Trim();
if (string.IsNullOrEmpty(f))
return null;
var set = new HashSet();
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;
}
}
}