using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace TKS
{
///
/// UI 테마 적용 도우미(PD #1100) — 아웃게임 UiKit · 전투 HUD · 월드맵 팝업이 같이 쓴다.
/// 테마가 없으면 모든 함수가 false/null 을 돌려주고 호출부는 기존 도형으로 그린다.
///
public static class UISkin
{
public const string OverlayName = "Gloss";
public static UITheme Theme
{
get { return UITheme.Current; }
}
public static bool On
{
get { return Theme != null; }
}
public static Color Col(string id, Color fallback)
{
Color c;
return Theme != null && Theme.TryColor(id, out c) ? c : fallback;
}
public static Font TitleFont
{
get { return Theme != null && Theme.titleFont != null ? Theme.titleFont : TextUtil.Font; }
}
public static bool Has(string skinId)
{
return Theme != null && Theme.Skin(skinId) != null;
}
/// 이미지에 스킨(9-slice 바탕 + 덮개) 적용 · 색은 호출부(img.color)가 정한다.
public static bool Apply(Image img, string skinId)
{
var s = Theme != null ? Theme.Skin(skinId) : null;
if (s == null || img == null) return false;
var sp = s.Sprite;
img.sprite = sp;
img.type = sp.border != Vector4.zero ? Image.Type.Sliced : Image.Type.Simple;
img.pixelsPerUnitMultiplier = s.ppu > 0f ? s.ppu : 1f;
img.fillCenter = true;
var old = img.transform.Find(OverlayName);
if (old != null) Object.Destroy(old.gameObject);
if (s.overlay != null) Overlay(img.transform, s.overlay, s.inset, s.ppu).color = new Color(1f, 1f, 1f, s.overlayAlpha);
return true;
}
/// 부모를 꽉 채우는 덮개(광택 등 · 첫 자식 = 내용 아래) · inset = 왼 · 위 · 오 · 아래.
public static Image Overlay(Transform parent, Sprite sp, Vector4 inset, float ppu = 1f)
{
var go = new GameObject(OverlayName, typeof(RectTransform));
go.transform.SetParent(parent, false);
go.transform.SetAsFirstSibling();
var rt = (RectTransform)go.transform;
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
rt.offsetMin = new Vector2(inset.x, inset.w);
rt.offsetMax = new Vector2(-inset.z, -inset.y);
var img = go.AddComponent();
img.sprite = sp;
img.type = sp.border != Vector4.zero ? Image.Type.Sliced : Image.Type.Simple;
img.pixelsPerUnitMultiplier = ppu > 0f ? ppu : 1f;
img.preserveAspect = sp.border == Vector4.zero;
img.raycastTarget = false;
return img;
}
public static Sprite Icon(string id)
{
return Theme != null ? Theme.Icon(id) : null;
}
/// 아이콘 이미지(테마에 id 가 없으면 null) · 비율 유지 · 표 tint 적용.
public static Image IconImage(Transform parent, string id, Vector2 anchor, Vector2 pivot, Vector2 pos, Vector2 size)
{
var e = Theme != null ? Theme.IconEntryOf(id) : null;
if (e == null) return null;
var go = new GameObject("Icon_" + id, typeof(RectTransform));
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform;
rt.anchorMin = rt.anchorMax = anchor;
rt.pivot = pivot;
rt.anchoredPosition = pos;
rt.sizeDelta = size;
var img = go.AddComponent();
img.sprite = e.Sprite;
img.color = e.samguk != null ? Color.white : e.tint;
img.preserveAspect = true;
img.raycastTarget = false;
return img;
}
/// 글자 외곽선(팩 버튼 글자처럼 굵은 어두운 선).
public static void Stroke(Graphic g, Color c, float d = 2.5f)
{
if (g == null || g.GetComponent() != null) return;
var t = g as Text;
if (t != null && t.fontSize < 40) d = Mathf.Min(d, t.fontSize < 30 ? 1.5f : 2f); // 작은 글자는 얇게(굵으면 한글이 뭉개짐)
var o = g.gameObject.AddComponent();
o.effectColor = c;
o.effectDistance = new Vector2(d, -d);
}
static readonly Dictionary worldCache = new Dictionary();
///
/// 월드 공간(SpriteRenderer · 9-slice)용 스킨 사본 — 같은 텍스처를 ppu 만 바꿔 다시 만든다(체력 바처럼 월드 크기가 작은 막대가 테두리보다 작아 깨지지 않게).
///
public static Sprite WorldSprite(string skinId, float ppu)
{
var s = Theme != null ? Theme.Skin(skinId) : null;
if (s == null) return null;
string key = skinId + "@" + ppu;
Sprite sp;
if (worldCache.TryGetValue(key, out sp) && sp != null) return sp;
var src = s.Sprite;
// SpriteRenderer 9-slice 는 가운데 칸이 0 px 이면 가운데를 그리지 않는다(팩 슬라이더 = 테두리가 전부) → 가운데 2 px 를 남긴다
var b = src.border;
float w = src.rect.width, h = src.rect.height;
if (b.x + b.z > w - 2f)
{
float k = Mathf.Max(0f, w - 2f) / Mathf.Max(1f, b.x + b.z);
b.x = Mathf.Floor(b.x * k);
b.z = Mathf.Floor(b.z * k);
}
if (b.y + b.w > h - 2f)
{
float k = Mathf.Max(0f, h - 2f) / Mathf.Max(1f, b.y + b.w);
b.y = Mathf.Floor(b.y * k);
b.w = Mathf.Floor(b.w * k);
}
// 아틀라스에 묶인 스프라이트(PD 2026-11-10 UI 아틀라스) = texture 가 아틀라스 장 → 그 장 안의 자리(textureRect)로 잘라야 한다(rect = 원본 기준)
var area = src.packed ? src.textureRect : src.rect;
sp = Sprite.Create(src.texture, area, new Vector2(0.5f, 0.5f), ppu, 0, SpriteMeshType.FullRect, b);
sp.name = key;
sp.hideFlags = HideFlags.DontSave;
worldCache[key] = sp;
return sp;
}
}
}