using System; using HarmonyLib; using System.Collections.Generic; using System.Globalization; using BepInEx.Configuration; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; using UXAssist.Common; using Object = UnityEngine.Object; namespace UXAssist.UI; // MyWindow modified from LSTM: https://github.com/hetima/DSP_LSTM/blob/main/LSTM/MyWindowCtl.cs public class MyWindow : ManualBehaviour { private float _maxX; protected float MaxY; protected const float TitleHeight = 48f; protected const float TabWidth = 105f; protected const float TabHeight = 27f; protected const float Margin = 30f; protected const float Spacing = 10f; public event Action OnFree; public override void _OnFree() { OnFree?.Invoke(); } public virtual void TryClose() { _Close(); } public virtual bool IsWindowFunctional() { return true; } public void Open() { _Open(); transform.SetAsLastSibling(); } public void Close() => _Close(); public void SetTitle(string title) { var txt = gameObject.transform.Find("panel-bg/title-text")?.gameObject.GetComponent(); if (txt) { txt.text = title.Translate(); } } public void AutoFitWindowSize() { var trans = GetComponent(); trans.sizeDelta = new Vector2(_maxX + Margin + TabWidth + Spacing + Margin, MaxY + TitleHeight + Margin); } private static void AddElement(float x, float y, RectTransform rect, RectTransform parent = null) { if (rect != null) { Util.NormalizeRectWithTopLeft(rect, x, y, parent); } } public static Text AddText(float x, float y, RectTransform parent, string label, int fontSize = 14, string objName = "label") { var src = UIRoot.instance.uiGame.assemblerWindow.stateText; var txt = Instantiate(src); txt.gameObject.name = objName; txt.text = label.Translate(); txt.color = new Color(1f, 1f, 1f, 0.4f); txt.alignment = TextAnchor.MiddleLeft; txt.fontSize = fontSize; txt.rectTransform.sizeDelta = new Vector2(txt.preferredWidth + 8f, txt.preferredHeight + 8f); AddElement(x, y, txt.rectTransform, parent); return txt; } public Text AddText2(float x, float y, RectTransform parent, string label, int fontSize = 14, string objName = "label") { var text = AddText(x, y, parent, label, fontSize, objName); _maxX = Math.Max(_maxX, x + text.rectTransform.sizeDelta.x); MaxY = Math.Max(MaxY, y + text.rectTransform.sizeDelta.y); return text; } public static UIButton AddTipsButton(float x, float y, RectTransform parent, string label, string tip, string content, string objName = "tips-button") { var src = UIRoot.instance.galaxySelect.sandboxToggle.gameObject.transform.parent.Find("tip-button"); var dst = Instantiate(src); dst.gameObject.name = objName; var btn = dst.GetComponent(); Util.NormalizeRectWithTopLeft(btn, x, y, parent); btn.tips.topLevel = true; btn.tips.tipTitle = label; btn.tips.tipText = tip; btn.UpdateTip(); return btn; } public UIButton AddTipsButton2(float x, float y, RectTransform parent, string label, string tip, string content, string objName = "tips-button") { var tipsButton = AddTipsButton(x, y, parent, label, tip, content, objName); var rect = tipsButton.transform as RectTransform; if (rect != null) { _maxX = Math.Max(_maxX, x + rect.sizeDelta.x); MaxY = Math.Max(MaxY, y + rect.sizeDelta.y); } return tipsButton; } public UIButton AddButton(float x, float y, RectTransform parent, string text = "", int fontSize = 16, string objName = "button", UnityAction onClick = null) { return AddButton(x, y, 150f, parent, text, fontSize, objName, onClick); } public UIButton AddButton(float x, float y, float width, RectTransform parent, string text = "", int fontSize = 16, string objName = "button", UnityAction onClick = null) { var panel = UIRoot.instance.uiGame.statWindow.performancePanelUI; var btn = Instantiate(panel.cpuActiveButton); btn.gameObject.name = objName; var rect = Util.NormalizeRectWithTopLeft(btn, x, y, parent); rect.sizeDelta = new Vector2(width, rect.sizeDelta.y); var l = btn.gameObject.transform.Find("button-text").GetComponent(); var t = btn.gameObject.transform.Find("button-text").GetComponent(); if (l != null) { l.stringKey = text; l.translation = text.Translate(); } if (t != null) { t.text = text.Translate(); } t.fontSize = fontSize; btn.tip = null; btn.tips = new UIButton.TipSettings(); btn.button.onClick.RemoveAllListeners(); if (onClick != null) btn.button.onClick.AddListener(onClick); _maxX = Math.Max(_maxX, x + rect.sizeDelta.x); MaxY = Math.Max(MaxY, y + rect.sizeDelta.y); return btn; } public UIButton AddFlatButton(float x, float y, RectTransform parent, string text = "", int fontSize = 12, string objName = "button", UnityAction onClick = null) { var panel = UIRoot.instance.uiGame.dysonEditor.controlPanel.hierarchy.layerPanel; var btn = Instantiate(panel.layerButtons[0]); btn.gameObject.name = objName; btn.highlighted = false; var img = btn.GetComponent(); if (img != null) { img.sprite = panel.buttonDefaultSprite; img.color = new Color(img.color.r, img.color.g, img.color.b, 13f / 255f); } img = btn.gameObject.transform.Find("frame")?.GetComponent(); if (img != null) { img.color = new Color(img.color.r, img.color.g, img.color.b, 0f); } var rect = Util.NormalizeRectWithTopLeft(btn, x, y, parent); var t = btn.gameObject.transform.Find("Text")?.GetComponent(); if (t != null) { t.text = text.Translate(); t.fontSize = fontSize; } btn.button.onClick.RemoveAllListeners(); if (onClick != null) btn.button.onClick.AddListener(onClick); _maxX = Math.Max(_maxX, x + rect.sizeDelta.x); MaxY = Math.Max(MaxY, y + rect.sizeDelta.y); return btn; } public MyCheckBox AddCheckBox(float x, float y, RectTransform parent, ConfigEntry config, string label = "", int fontSize = 15) { var cb = MyCheckBox.CreateCheckBox(x, y, parent, config, label, fontSize); _maxX = Math.Max(_maxX, x + cb.Width); MaxY = Math.Max(MaxY, y + cb.Height); return cb; } public MySlider AddSlider(float x, float y, RectTransform parent, float value, float minValue, float maxValue, string format = "G", float width = 0f) { var slider = MySlider.CreateSlider(x, y, parent, value, minValue, maxValue, format, width); var rect = slider.rectTrans; if (rect != null) { _maxX = Math.Max(_maxX, x + rect.sizeDelta.x); MaxY = Math.Max(MaxY, y + rect.sizeDelta.y); } return slider; } public class ValueMapper { public virtual int Min => 1; public virtual int Max => 100; public virtual int ValueToIndex(T value) => (int)Convert.ChangeType(value, typeof(int), CultureInfo.InvariantCulture); public virtual T IndexToValue(int index) => (T)Convert.ChangeType(index, typeof(T), CultureInfo.InvariantCulture); public virtual string FormatValue(string format, T value) { return string.Format($"{{0:{format}}}", value); } } public MySlider AddSlider(float x, float y, RectTransform parent, ConfigEntry config, ValueMapper valueMapper, string format = "G", float width = 0f) { var slider = MySlider.CreateSlider(x, y, parent, OnConfigValueChanged(config), valueMapper.Min, valueMapper.Max, format, width); slider.SetLabelText(valueMapper.FormatValue(format, config.Value)); EventHandler func = (_, _) => { var index = OnConfigValueChanged(config); slider.Value = index; slider.SetLabelText(valueMapper.FormatValue(format, config.Value)); }; config.SettingChanged += func; OnFree += () => config.SettingChanged -= func; slider.OnValueChanged += () => { var index = Mathf.RoundToInt(slider.Value); config.Value = valueMapper.IndexToValue(index); slider.SetLabelText(valueMapper.FormatValue(format, config.Value)); }; var rect = slider.rectTrans; if (rect != null) { _maxX = Math.Max(_maxX, x + rect.sizeDelta.x); MaxY = Math.Max(MaxY, y + rect.sizeDelta.y); } return slider; int OnConfigValueChanged(ConfigEntry conf) { var index = valueMapper.ValueToIndex(conf.Value); if (index >= 0) return index; index = ~index; index = Math.Max(0, Math.Min(valueMapper.Max, index)); conf.Value = valueMapper.IndexToValue(index); return index; } } private class ArrayMapper : ValueMapper { private readonly T[] _values; public ArrayMapper(T[] values) { Array.Sort(values); _values = values; } public override int Min => 0; public override int Max => _values.Length - 1; public override int ValueToIndex(T value) { return Array.BinarySearch(_values, value); } public override T IndexToValue(int index) { return _values[index >= 0 && index < _values.Length ? index : 0]; } } public MySlider AddSlider(float x, float y, RectTransform parent, ConfigEntry config, T[] valueList, string format = "G", float width = 0f) { return AddSlider(x, y, parent, config, new ArrayMapper(valueList), format, width); } public InputField AddInputField(float x, float y, RectTransform parent, string text = "", int fontSize = 16, string objName = "input", UnityAction onChanged = null, UnityAction onEditEnd = null) { var stationWindow = UIRoot.instance.uiGame.stationWindow; //public InputField nameInput; var inputField = Instantiate(stationWindow.nameInput); inputField.gameObject.name = objName; Destroy(inputField.GetComponent()); inputField.GetComponent().color = new Color(1f, 1f, 1f, 0.05f); var rect = Util.NormalizeRectWithTopLeft(inputField, x, y, parent); rect.sizeDelta = new Vector2(210, rect.sizeDelta.y); inputField.text = text; inputField.textComponent.fontSize = fontSize; inputField.onValueChanged.RemoveAllListeners(); if (onChanged != null) inputField.onValueChanged.AddListener(onChanged); inputField.onEndEdit.RemoveAllListeners(); if (onEditEnd != null) inputField.onEndEdit.AddListener(onEditEnd); _maxX = Math.Max(_maxX, x + rect.sizeDelta.x); MaxY = Math.Max(MaxY, y + rect.sizeDelta.y); return inputField; } public InputField AddInputField(float x, float y, float width, RectTransform parent, ConfigEntry config, int fontSize = 16, string objName = "input") { var stationWindow = UIRoot.instance.uiGame.stationWindow; //public InputField nameInput; var inputField = Instantiate(stationWindow.nameInput); inputField.gameObject.name = objName; Destroy(inputField.GetComponent()); inputField.GetComponent().color = new Color(1f, 1f, 1f, 0.05f); var rect = Util.NormalizeRectWithTopLeft(inputField, x, y, parent); rect.sizeDelta = new Vector2(width, rect.sizeDelta.y); inputField.text = config.Value; inputField.textComponent.fontSize = fontSize; inputField.onValueChanged.RemoveAllListeners(); inputField.onEndEdit.RemoveAllListeners(); inputField.onEndEdit.AddListener(value => config.Value = value); _maxX = Math.Max(_maxX, x + rect.sizeDelta.x); MaxY = Math.Max(MaxY, y + rect.sizeDelta.y); return inputField; } } public class MyWindowWithTabs : MyWindow { private readonly List> _tabs = []; private float _tabY = 54f; public override void TryClose() { _Close(); } public override bool IsWindowFunctional() { return true; } private RectTransform AddTabInternal(float y, int index, RectTransform parent, string label) { var tab = new GameObject(); var tabRect = tab.AddComponent(); Util.NormalizeRectWithMargin(tabRect, TitleHeight, Margin + TabWidth + Spacing, 0f, 0f, parent); tab.name = "tab-" + index; var swarmPanel = UIRoot.instance.uiGame.dysonEditor.controlPanel.hierarchy.swarmPanel; var src = swarmPanel.orbitButtons[0]; var btn = Instantiate(src); var btnRect = Util.NormalizeRectWithTopLeft(btn, Margin, y, parent); btn.name = "tab-btn-" + index; btnRect.sizeDelta = new Vector2(TabWidth, TabHeight); btn.transform.Find("frame").gameObject.SetActive(false); if (btn.transitions.Length >= 3) { btn.transitions[0].normalColor = new Color(0.1f, 0.1f, 0.1f, 0.68f); btn.transitions[0].highlightColorOverride = new Color(0.9906f, 0.5897f, 0.3691f, 0.4f); btn.transitions[1].normalColor = new Color(1f, 1f, 1f, 0.6f); btn.transitions[1].highlightColorOverride = new Color(0.2f, 0.1f, 0.1f, 0.9f); } var btnText = btn.transform.Find("Text").GetComponent(); btnText.text = label.Translate(); btnText.fontSize = 16; btn.data = index; _tabs.Add(Tuple.Create(tabRect, btn)); btn.onClick += OnTabButtonClick; MaxY = Math.Max(MaxY, y + TabHeight); return tabRect; } public RectTransform AddTab(RectTransform parent, string label) { var result = AddTabInternal(_tabY, _tabs.Count, parent, label); _tabY += 28f; return result; } public void AddSplitter(RectTransform parent, float spacing) { var img = Instantiate(UIRoot.instance.optionWindow.transform.Find("tab-line").Find("bar")); Destroy(img.Find("tri").gameObject); _tabY += spacing; var rect = Util.NormalizeRectWithTopLeft(img, 28, _tabY, parent); rect.sizeDelta = new Vector2(107, 2); _tabY += 2; } public void AddTabGroup(RectTransform parent, string label, string objName = "tabl-group-label") { AddText(28, _tabY - 2, parent, label, 16, objName); _tabY += 28f; } protected void SetCurrentTab(int index) => OnTabButtonClick(index); private void OnTabButtonClick(int index) { foreach (var (rectTransform, btn) in _tabs) { if (btn.data != index) { btn.highlighted = false; rectTransform.gameObject.SetActive(false); continue; } btn.highlighted = true; rectTransform.gameObject.SetActive(true); } } } public class MyWindowManager { private static readonly List Windows = new(4); private static bool _initialized; public static void Enable(bool on) { Patch.Enable(on); } public static T CreateWindow(string name, string title = "") where T : MyWindow { var srcWin = UIRoot.instance.uiGame.tankWindow; var src = srcWin.gameObject; var go = Object.Instantiate(src, UIRoot.instance.uiGame.transform.parent); go.name = name; go.SetActive(false); Object.Destroy(go.GetComponent()); var win = go.AddComponent() as MyWindow; if (win == null) return null; for (var i = 0; i < go.transform.childCount; i++) { var child = go.transform.GetChild(i).gameObject; if (child.name == "panel-bg") { var btn = child.GetComponentInChildren