SHA256
324 lines
13 KiB
C#
324 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using UnityEngine.UIElements;
|
|
using Button = UnityEngine.UI.Button;
|
|
using Image = UnityEngine.UI.Image;
|
|
|
|
namespace ADOFAIRenderer.UI
|
|
{
|
|
public class DialogButton
|
|
{
|
|
public string Text;
|
|
public Action OnClick;
|
|
public Color? BackgroundColor;
|
|
|
|
public DialogButton(string text, Action onClick, Color? backgroundColor = null)
|
|
{
|
|
Text = text;
|
|
OnClick = onClick;
|
|
BackgroundColor = backgroundColor;
|
|
}
|
|
}
|
|
|
|
public class CommonWarningDialogUI : MonoBehaviour
|
|
{
|
|
public static CommonWarningDialogUI? Instance { get; private set; }
|
|
private readonly List<VisualElement> _disabledElements = new();
|
|
|
|
private void Awake()
|
|
{
|
|
Instance = this;
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
foreach (var elem in _disabledElements)
|
|
{
|
|
if (elem != null)
|
|
{
|
|
elem.SetEnabled(true);
|
|
}
|
|
}
|
|
_disabledElements.Clear();
|
|
|
|
if (Instance == this)
|
|
{
|
|
Instance = null;
|
|
}
|
|
}
|
|
|
|
private static TMP_FontAsset GetNanumSquareNeoBdFontAsset()
|
|
{
|
|
TMP_FontAsset? font = null;
|
|
if (RenderMod.FontBundle)
|
|
{
|
|
try
|
|
{
|
|
font = RenderMod.FontBundle.LoadAsset<TMP_FontAsset>("NanumSquareNeoOTF-Bd SDF");
|
|
}
|
|
catch
|
|
{
|
|
// 로딩 실패
|
|
}
|
|
}
|
|
|
|
// 폴백 처리
|
|
if (!font)
|
|
{
|
|
try
|
|
{
|
|
font = RDString.fontData.fontTMP;
|
|
}
|
|
catch
|
|
{
|
|
// ignored
|
|
}
|
|
}
|
|
if (!font)
|
|
{
|
|
// ReSharper disable once Unity.UnknownResource
|
|
font = Resources.Load<TMP_FontAsset>("Fonts & Materials/LiberationSans SDF");
|
|
}
|
|
|
|
if (font) return font;
|
|
var fonts = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
|
|
if (fonts.Length > 0) font = fonts[0];
|
|
|
|
return font!;
|
|
}
|
|
|
|
public static void ShowDialog(string title, string description, params DialogButton[] buttons)
|
|
{
|
|
if (buttons.Length == 0) return;
|
|
|
|
var obj = new GameObject("CommonWarningDialogUI");
|
|
DontDestroyOnLoad(obj);
|
|
var dialog = obj.AddComponent<CommonWarningDialogUI>();
|
|
dialog.Initialize(title, description, buttons);
|
|
}
|
|
|
|
private void Initialize(string title, string description, DialogButton[] buttons)
|
|
{
|
|
if (!UnityEngine.EventSystems.EventSystem.current)
|
|
{
|
|
var eventSystemObj = new GameObject("EventSystem");
|
|
eventSystemObj.transform.SetParent(transform);
|
|
eventSystemObj.AddComponent<UnityEngine.EventSystems.EventSystem>();
|
|
eventSystemObj.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>();
|
|
}
|
|
|
|
foreach (var doc in FindObjectsByType<UIDocument>(FindObjectsSortMode.None))
|
|
{
|
|
if (doc && doc.rootVisualElement != null && doc.rootVisualElement.enabledSelf)
|
|
{
|
|
doc.rootVisualElement.SetEnabled(false);
|
|
_disabledElements.Add(doc.rootVisualElement);
|
|
}
|
|
}
|
|
|
|
// UI 레이어(5) 설정
|
|
gameObject.layer = 5;
|
|
|
|
// Canvas 설정
|
|
var canvas = gameObject.AddComponent<Canvas>();
|
|
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
|
canvas.sortingOrder = 32766; // FFmpegDownloadUI(32767) 바로 아래
|
|
|
|
// 해상도 대응 캔버스 스케일러 설정
|
|
var scaler = gameObject.AddComponent<CanvasScaler>();
|
|
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
|
scaler.referenceResolution = new Vector2(1920, 1080);
|
|
scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
|
|
scaler.matchWidthOrHeight = 0.5f;
|
|
|
|
gameObject.AddComponent<GraphicRaycaster>();
|
|
|
|
// 1. 전체 화면 오버레이 배경 생성
|
|
var bgPanelObj = new GameObject("BackgroundPanel");
|
|
bgPanelObj.transform.SetParent(transform, false);
|
|
bgPanelObj.layer = 5;
|
|
|
|
var bgImage = bgPanelObj.AddComponent<Image>();
|
|
bgImage.color = new Color(0f, 0f, 0f, 0.65f); // 어두운 투명 오버레이
|
|
bgImage.raycastTarget = true;
|
|
|
|
var bgRect = bgPanelObj.GetComponent<RectTransform>();
|
|
bgRect.anchorMin = Vector2.zero;
|
|
bgRect.anchorMax = Vector2.one;
|
|
bgRect.offsetMin = Vector2.zero;
|
|
bgRect.offsetMax = Vector2.zero;
|
|
|
|
var font = GetNanumSquareNeoBdFontAsset();
|
|
|
|
// 2. 모달 컨테이너 생성
|
|
var modalContainerObj = new GameObject("ModalContainer");
|
|
modalContainerObj.transform.SetParent(bgPanelObj.transform, false);
|
|
modalContainerObj.layer = 5;
|
|
|
|
var modalImage = modalContainerObj.AddComponent<Image>();
|
|
modalImage.color = new Color(0.08f, 0.08f, 0.1f, 0.88f); // 기존 UI의 배경 톤과 동일
|
|
|
|
var modalOutline = modalContainerObj.AddComponent<Outline>();
|
|
modalOutline.effectColor = Color.white;
|
|
modalOutline.effectDistance = new Vector2(3.0f, -3.0f); // 기존 UI 외곽선 스타일 일관성
|
|
|
|
var modalRect = modalContainerObj.GetComponent<RectTransform>();
|
|
modalRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
modalRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
modalRect.pivot = new Vector2(0.5f, 0.5f);
|
|
modalRect.sizeDelta = new Vector2(650, 360);
|
|
|
|
// 3. 타이틀 텍스트 생성
|
|
var titleObj = new GameObject("TitleText");
|
|
titleObj.transform.SetParent(modalContainerObj.transform, false);
|
|
titleObj.layer = 5;
|
|
|
|
var titleText = titleObj.AddComponent<TextMeshProUGUI>();
|
|
titleText.text = title;
|
|
titleText.fontSize = 24;
|
|
titleText.font = font;
|
|
titleText.color = new Color(1.0f, 0.8f, 0.2f, 1.0f); // 경고 테마에 맞는 주황/황색 계열
|
|
titleText.alignment = TextAlignmentOptions.Center;
|
|
|
|
var titleRect = titleObj.GetComponent<RectTransform>();
|
|
titleRect.anchorMin = new Vector2(0, 1);
|
|
titleRect.anchorMax = new Vector2(1, 1);
|
|
titleRect.pivot = new Vector2(0.5f, 1);
|
|
titleRect.anchoredPosition = new Vector2(0, -35);
|
|
titleRect.sizeDelta = new Vector2(0, 40);
|
|
|
|
// 4. 본문 설명 텍스트 생성
|
|
var descObj = new GameObject("DescriptionText");
|
|
descObj.transform.SetParent(modalContainerObj.transform, false);
|
|
descObj.layer = 5;
|
|
|
|
var descText = descObj.AddComponent<TextMeshProUGUI>();
|
|
descText.text = description;
|
|
descText.fontSize = 20;
|
|
descText.font = font;
|
|
descText.color = new Color(0.85f, 0.85f, 0.85f, 1f);
|
|
descText.alignment = TextAlignmentOptions.Center;
|
|
descText.lineSpacing = 8;
|
|
|
|
var descRect = descObj.GetComponent<RectTransform>();
|
|
descRect.anchorMin = new Vector2(0, 0.5f);
|
|
descRect.anchorMax = new Vector2(1, 1);
|
|
descRect.pivot = new Vector2(0.5f, 0.5f);
|
|
descRect.anchoredPosition = new Vector2(0, -65);
|
|
descRect.sizeDelta = new Vector2(-40, 120);
|
|
|
|
// 5. 버튼 컨테이너 생성
|
|
var buttonContainerObj = new GameObject("ButtonContainer", typeof(RectTransform));
|
|
buttonContainerObj.transform.SetParent(modalContainerObj.transform, false);
|
|
buttonContainerObj.layer = 5;
|
|
|
|
var btnContainerRect = buttonContainerObj.GetComponent<RectTransform>();
|
|
btnContainerRect.anchorMin = new Vector2(0, 0);
|
|
btnContainerRect.anchorMax = new Vector2(1, 0);
|
|
btnContainerRect.pivot = new Vector2(0.5f, 0);
|
|
btnContainerRect.anchoredPosition = new Vector2(0, 35);
|
|
btnContainerRect.sizeDelta = new Vector2(0, 50);
|
|
|
|
// 6. 가변 버튼 동적 배치 및 스타일링
|
|
float totalWidth = 560f;
|
|
float spacing = 15f;
|
|
float buttonHeight = 45f;
|
|
int numButtons = buttons.Length;
|
|
|
|
float buttonWidth = numButtons == 1
|
|
? 200f
|
|
: (totalWidth - (spacing * (numButtons - 1))) / numButtons;
|
|
|
|
float startX = numButtons == 1
|
|
? 0
|
|
: (-totalWidth / 2f) + (buttonWidth / 2f);
|
|
|
|
for (int i = 0; i < numButtons; i++)
|
|
{
|
|
var btnData = buttons[i];
|
|
var btnObj = new GameObject($"Button_{i}");
|
|
btnObj.transform.SetParent(buttonContainerObj.transform, false);
|
|
btnObj.layer = 5;
|
|
|
|
// 기본 색상 정의
|
|
Color baseColor = btnData.BackgroundColor ?? new Color(0.2f, 0.2f, 0.23f, 1f); // 커스텀 색상 또는 회색톤 기본값
|
|
Color hoverColor = ApplyColorBrightness(baseColor, 1.2f);
|
|
Color pressedColor = ApplyColorBrightness(baseColor, 0.8f);
|
|
|
|
var btnImage = btnObj.AddComponent<Image>();
|
|
btnImage.color = baseColor;
|
|
btnImage.raycastTarget = true;
|
|
|
|
var btn = btnObj.AddComponent<Button>();
|
|
SetupButtonStyle(btn, baseColor, hoverColor, pressedColor);
|
|
|
|
// 클릭 콜백 등록
|
|
btn.onClick.AddListener(() =>
|
|
{
|
|
try
|
|
{
|
|
btnData.OnClick();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MelonLoader.Melon<RenderMod>.Logger.Error($"Dialog Button Click Event Error: {ex}");
|
|
}
|
|
Destroy(gameObject);
|
|
});
|
|
|
|
var btnRect = btnObj.GetComponent<RectTransform>();
|
|
btnRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
btnRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
btnRect.pivot = new Vector2(0.5f, 0.5f);
|
|
|
|
float posX = numButtons == 1
|
|
? 0
|
|
: startX + i * (buttonWidth + spacing);
|
|
|
|
btnRect.anchoredPosition = new Vector2(posX, 0);
|
|
btnRect.sizeDelta = new Vector2(buttonWidth, buttonHeight);
|
|
|
|
// 텍스트 라벨 추가
|
|
var txtObj = new GameObject("Text");
|
|
txtObj.transform.SetParent(btnObj.transform, false);
|
|
txtObj.layer = 5;
|
|
|
|
var text = txtObj.AddComponent<TextMeshProUGUI>();
|
|
text.text = btnData.Text;
|
|
text.fontSize = 20;
|
|
text.font = font;
|
|
text.color = Color.white;
|
|
text.alignment = TextAlignmentOptions.Center;
|
|
|
|
var txtRect = txtObj.GetComponent<RectTransform>();
|
|
txtRect.anchorMin = Vector2.zero;
|
|
txtRect.anchorMax = Vector2.one;
|
|
txtRect.offsetMin = Vector2.zero;
|
|
txtRect.offsetMax = Vector2.zero;
|
|
}
|
|
}
|
|
|
|
private void SetupButtonStyle(Button button, Color normalColor, Color hoverColor, Color pressedColor)
|
|
{
|
|
button.transition = Selectable.Transition.ColorTint;
|
|
var colors = button.colors;
|
|
colors.normalColor = normalColor;
|
|
colors.highlightedColor = hoverColor;
|
|
colors.pressedColor = pressedColor;
|
|
colors.disabledColor = new Color(0.3f, 0.3f, 0.3f, 1f);
|
|
button.colors = colors;
|
|
}
|
|
|
|
private Color ApplyColorBrightness(Color color, float factor)
|
|
{
|
|
float r = Mathf.Clamp01(color.r * factor);
|
|
float g = Mathf.Clamp01(color.g * factor);
|
|
float b = Mathf.Clamp01(color.b * factor);
|
|
return new Color(r, g, b, color.a);
|
|
}
|
|
}
|
|
}
|