using System; using System.Collections.Generic; using MelonLoader; using O5Kit.Core; using O5Kit.Factory; using TMPro; using UnityEngine; using UnityEngine.UI; using UnityEngine.UIElements; using Image = UnityEngine.UI.Image; namespace ADOFAIRenderer.UI; internal class DialogButton { // ReSharper disable FieldCanBeMadeReadOnly.Global public string Text; public Action OnClick; public Color? BackgroundColor; // ReSharper restore FieldCanBeMadeReadOnly.Global public DialogButton(string text, Action onClick, Color? backgroundColor = null) { Text = text; OnClick = onClick; BackgroundColor = backgroundColor; } } internal class CommonWarningDialogUI : MonoBehaviour { public static CommonWarningDialogUI? Instance { get; private set; } private readonly List _disabledElements = new(); private O5Window? _window; private void Awake() { Instance = this; } private void OnDestroy() { _window?.Dispose(); _window = null; foreach (var elem in _disabledElements) { if (elem != null) { elem.SetEnabled(true); } } _disabledElements.Clear(); if (Instance == this) { Instance = null; } } 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(); 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(); eventSystemObj.AddComponent(); } foreach (var doc in FindObjectsByType(FindObjectsSortMode.None)) { if (doc && doc.rootVisualElement is { enabledSelf: true }) { doc.rootVisualElement.SetEnabled(false); _disabledElements.Add(doc.rootVisualElement); } } // Set UI layer (5) gameObject.layer = 5; // Canvas setup var canvas = gameObject.AddComponent(); canvas.renderMode = RenderMode.ScreenSpaceOverlay; canvas.sortingOrder = 32766; var scaler = gameObject.AddComponent(); scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; scaler.referenceResolution = new Vector2(1920, 1080); scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight; scaler.matchWidthOrHeight = 0.5f; gameObject.AddComponent(); var ctx = RendererUIManager.Context; // 1. Create full screen backdrop (scrim) to block background input var bgPanelObj = new GameObject("Backdrop"); bgPanelObj.transform.SetParent(transform, false); bgPanelObj.layer = 5; var bgImage = bgPanelObj.AddComponent(); bgImage.color = new Color(0f, 0f, 0f, 0.65f); bgImage.raycastTarget = true; var bgRect = bgPanelObj.GetComponent(); bgRect.anchorMin = Vector2.zero; bgRect.anchorMax = Vector2.one; bgRect.offsetMin = Vector2.zero; bgRect.offsetMax = Vector2.zero; // 2. Create actual native O5Window _window = O5Window.Create(ctx, bgPanelObj.transform, new O5WindowOptions { Id = "RendererMod_WarningDialog", Title = title, Size = new Vector2(540, 240), Draggable = true, Resizable = false, ShowCloseButton = false, ShowOutline = true, Padding = new RectOffset(16, 16, 16, 16) }); // 3. Build content inside window.Content var content = _window.Content; var vLayout = content.gameObject.AddComponent(); vLayout.spacing = 16f; vLayout.padding = new RectOffset(16, 16, 12, 12); vLayout.childControlWidth = true; vLayout.childControlHeight = true; vLayout.childForceExpandWidth = true; vLayout.childForceExpandHeight = false; // Description text var descText = O5Factory.ControlText(ctx, content, 18f, noPad: true); descText.text = description; descText.alignment = TextAlignmentOptions.Center; descText.textWrappingMode = TextWrappingModes.Normal; descText.lineSpacing = 6; descText.color = new Color(0.88f, 0.88f, 0.88f, 1f); // ReSharper disable once InconsistentNaming var descLE = descText.gameObject.AddComponent(); descLE.flexibleWidth = 1f; descLE.flexibleHeight = 1f; // Button action row var btnRowObj = new GameObject("ButtonRow"); btnRowObj.transform.SetParent(content, false); var btnRowRect = btnRowObj.AddComponent(); var btnRowLE = btnRowObj.AddComponent(); btnRowLE.preferredHeight = 40f; btnRowLE.flexibleWidth = 1f; var hLayout = btnRowObj.AddComponent(); hLayout.spacing = 10f; hLayout.childAlignment = TextAnchor.MiddleCenter; hLayout.childControlWidth = true; hLayout.childControlHeight = true; hLayout.childForceExpandWidth = true; hLayout.childForceExpandHeight = true; for (var i = 0; i < buttons.Length; i++) { var btnData = buttons[i]; var btn = O5Factory.Button( ctx: ctx, parent: btnRowRect, onClick: () => { try { btnData.OnClick(); } catch (Exception ex) { Melon.Logger.Error($"Dialog Button Click Event Error: {ex}"); } Destroy(gameObject); }, text: btnData.Text, id: $"btn_dialog_{i}", height: 40f ); if (btnData.BackgroundColor.HasValue) { btn.NormalColor = btnData.BackgroundColor.Value; btn.UpdateVisual(); } var le = btn.Rect.gameObject.AddComponent(); le.flexibleWidth = 1f; le.preferredHeight = 40f; } } }