From a6babfcf0a4cb1325c040af9ff51a23f02dce4e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Thu, 25 Jul 2024 08:23:42 +0200 Subject: [PATCH 1/4] feat: added Setting structure --- JotunnLib/Entities/CustomPiece.cs | 36 ++++++++++++++- JotunnLib/Managers/PieceManager.cs | 32 +++++++++++++ JotunnLib/Settings/BepInExSetting.cs | 67 ++++++++++++++++++++++++++++ JotunnLib/Settings/Setting.cs | 45 +++++++++++++++++++ 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 JotunnLib/Settings/BepInExSetting.cs create mode 100644 JotunnLib/Settings/Setting.cs diff --git a/JotunnLib/Entities/CustomPiece.cs b/JotunnLib/Entities/CustomPiece.cs index b28b837b0..66d2131c4 100644 --- a/JotunnLib/Entities/CustomPiece.cs +++ b/JotunnLib/Entities/CustomPiece.cs @@ -2,6 +2,7 @@ using System.Reflection; using Jotunn.Configs; using Jotunn.Managers; +using Jotunn.Settings; using Jotunn.Utils; using UnityEngine; @@ -50,7 +51,11 @@ public string Category /// Indicator if references from s will be replaced at runtime. /// public bool FixReference { get; set; } - + + public Setting SettingsEnabled { get; set; } + + public Setting CategorySetting { get; set; } + /// /// Indicator if references from configs should get replaced /// @@ -82,6 +87,7 @@ public CustomPiece(GameObject piecePrefab, string pieceTable, bool fixReference) Piece = piecePrefab.GetComponent(); PieceTable = pieceTable; FixReference = fixReference; + CreateSettings(); } /// @@ -101,6 +107,7 @@ public CustomPiece(GameObject piecePrefab, PieceConfig pieceConfig) : base(Assem Category = pieceConfig.Category; pieceConfig.Apply(piecePrefab); + CreateSettings(); } /// @@ -120,6 +127,7 @@ public CustomPiece(GameObject piecePrefab, bool fixReference, PieceConfig pieceC Category = pieceConfig.Category; pieceConfig.Apply(piecePrefab); + CreateSettings(); } /// @@ -147,6 +155,7 @@ public CustomPiece(AssetBundle assetBundle, string assetName, string pieceTable, Piece = PiecePrefab.GetComponent(); PieceTable = pieceTable; FixReference = fixReference; + CreateSettings(); } /// @@ -174,6 +183,7 @@ public CustomPiece(AssetBundle assetBundle, string assetName, PieceConfig pieceC Category = pieceConfig.Category; pieceConfig.Apply(PiecePrefab); + CreateSettings(); } /// @@ -201,6 +211,7 @@ public CustomPiece(AssetBundle assetBundle, string assetName, bool fixReference, Category = pieceConfig.Category; pieceConfig.Apply(PiecePrefab); + CreateSettings(); } /// @@ -226,6 +237,7 @@ public CustomPiece(string name, bool addZNetView, string pieceTable) : base(Asse Piece = PiecePrefab.AddComponent(); Piece.m_name = name; PieceTable = pieceTable; + CreateSettings(); } /// @@ -251,6 +263,7 @@ public CustomPiece(string name, bool addZNetView, PieceConfig pieceConfig) : bas Category = pieceConfig.Category; pieceConfig.Apply(PiecePrefab); + CreateSettings(); } /// @@ -275,6 +288,7 @@ public CustomPiece(string name, string baseName, string pieceTable) : base(Assem Piece = PiecePrefab.GetComponent(); PieceTable = pieceTable; + CreateSettings(); } /// @@ -300,6 +314,20 @@ public CustomPiece(string name, string baseName, PieceConfig pieceConfig) : base Category = pieceConfig.Category; pieceConfig.Apply(PiecePrefab); + CreateSettings(); + } + + private void CreateSettings() + { + SettingsEnabled = new BepInExSetting(SourceMod, PiecePrefab.name, "Enabled", false, "Enable this custom piece", 10); + SettingsEnabled.OnChanged += () => + { + BindSettings(); + ConfigManagerUtils.BuildSettingList(); + }; + + CategorySetting = new BepInExSetting(SourceMod, PiecePrefab.name, "Category", Category, "Category of this custom piece", 9); + CategorySetting.OnChanged += () => Category = CategorySetting.Value; } /// @@ -339,6 +367,12 @@ public bool IsValid() return valid; } + public void BindSettings() + { + SettingsEnabled?.Bind(); + CategorySetting?.UpdateBinding(SettingsEnabled?.Value ?? false); + } + /// /// Helper method to determine if a prefab with a given name is a custom piece created with Jötunn. /// diff --git a/JotunnLib/Managers/PieceManager.cs b/JotunnLib/Managers/PieceManager.cs index f26d8bdbc..831e5f922 100644 --- a/JotunnLib/Managers/PieceManager.cs +++ b/JotunnLib/Managers/PieceManager.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection.Emit; using BepInEx; +using BepInEx.Bootstrap; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; @@ -131,6 +132,9 @@ private static void Hud_LateUpdate() [HarmonyPatch(typeof(ObjectDB), nameof(ObjectDB.Awake)), HarmonyPostfix, HarmonyPriority(Priority.Last)] private static void InvokeOnPiecesRegistered(ObjectDB __instance) => Instance.InvokeOnPiecesRegistered(__instance); + [HarmonyPatch(typeof(FejdStartup), nameof(FejdStartup.Awake)), HarmonyPostfix] + public static void BindSettings() => Instance.BindSettings(); + [HarmonyPatch(typeof(Player), nameof(Player.OnSpawned)), HarmonyPostfix] private static void ReloadKnownRecipes(Player __instance) => Instance.ReloadKnownRecipes(__instance); @@ -466,6 +470,34 @@ public void RemovePiece(CustomPiece piece) } } + private void BindSettings() + { + Dictionary saveOnConfigSet = new Dictionary(); + + foreach (var piece in Pieces.Values) + { + if (!saveOnConfigSet.ContainsKey(piece.SourceMod)) + { + PluginInfo plugin = Chainloader.PluginInfos[piece.SourceMod.GUID]; + saveOnConfigSet[piece.SourceMod] = plugin.Instance.Config.SaveOnConfigSet; + plugin.Instance.Config.SaveOnConfigSet = false; + } + + piece.BindSettings(); + } + + foreach (var sourceMod in saveOnConfigSet.Keys) + { + PluginInfo plugin = Chainloader.PluginInfos[sourceMod.GUID]; + plugin.Instance.Config.SaveOnConfigSet = saveOnConfigSet[sourceMod]; + + if (plugin.Instance.Config.SaveOnConfigSet) + { + plugin.Instance.Config.Save(); + } + } + } + /// /// Loop all items in the game and get all PieceTables used (vanilla and custom ones). /// diff --git a/JotunnLib/Settings/BepInExSetting.cs b/JotunnLib/Settings/BepInExSetting.cs new file mode 100644 index 000000000..2a0bf77eb --- /dev/null +++ b/JotunnLib/Settings/BepInExSetting.cs @@ -0,0 +1,67 @@ +using BepInEx; +using BepInEx.Bootstrap; +using BepInEx.Configuration; + +namespace Jotunn.Settings +{ + public class BepInExSetting : Setting + { + public string Section { get; set; } + public string Key { get; set; } + public T DefaultValue { get; set; } + public string Description { get; set; } + public int Order { get; private set; } + public bool AdminOnly { get; set; } + + private ConfigEntry entry; + private ConfigurationManagerAttributes attributes; + + public BepInExSetting(BepInPlugin sourceMod, string section, string key, T defaultValue, string description, int order, bool adminOnly = true) : base(sourceMod) + { + Section = section; + Key = key; + DefaultValue = defaultValue; + Description = description; + Order = order; + AdminOnly = adminOnly; + } + + public override void Bind() + { + if (entry != null) + { + return; + } + + BaseUnityPlugin plugin = Chainloader.PluginInfos[SourceMod.GUID].Instance; + attributes = new ConfigurationManagerAttributes + { + IsAdminOnly = AdminOnly, + Order = Order, + }; + + entry = plugin.Config.Bind(Section, Key, DefaultValue, new ConfigDescription(Description, null, attributes)); + entry.SettingChanged += (sender, args) => Value = entry.Value; + Value = entry.Value; + } + + public override void Unbind() + { + if (entry == null) + { + return; + } + + BaseUnityPlugin plugin = Chainloader.PluginInfos[SourceMod.GUID].Instance; + plugin.Config.Remove(entry.Definition); + entry = null; + + if (plugin.Config.SaveOnConfigSet) + { + plugin.Config.Save(); + } + + Value = DefaultValue; + } + } +} diff --git a/JotunnLib/Settings/Setting.cs b/JotunnLib/Settings/Setting.cs new file mode 100644 index 000000000..14f438865 --- /dev/null +++ b/JotunnLib/Settings/Setting.cs @@ -0,0 +1,45 @@ +using System; +using BepInEx; + +namespace Jotunn.Settings +{ + public abstract class Setting + { + public BepInPlugin SourceMod { get; set; } + + private T value; + + public T Value + { + get => value; + set + { + this.value = value; + OnChanged?.Invoke(); + } + } + + public event Action OnChanged; + + public Setting(BepInPlugin sourceMod) + { + this.SourceMod = sourceMod; + } + + public void UpdateBinding(bool enabled) + { + if (enabled) + { + Bind(); + } + else + { + Unbind(); + } + } + + public abstract void Bind(); + + public abstract void Unbind(); + } +} From 0451099068b06c3921ddc5cf78bfab8b0ca393a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Thu, 25 Jul 2024 09:02:25 +0200 Subject: [PATCH 2/4] feat: added PieceTable setting --- JotunnLib/Configs/PieceTables.cs | 12 +++++ JotunnLib/Entities/CustomPiece.cs | 35 ++++++++++++-- JotunnLib/Managers/PieceManager.cs | 73 ++++++++++++++++++++++++------ 3 files changed, 100 insertions(+), 20 deletions(-) diff --git a/JotunnLib/Configs/PieceTables.cs b/JotunnLib/Configs/PieceTables.cs index 0c5f8d39d..30aadc99e 100644 --- a/JotunnLib/Configs/PieceTables.cs +++ b/JotunnLib/Configs/PieceTables.cs @@ -71,6 +71,18 @@ public static string GetInternalName(string pieceTable) return pieceTable; } + /// + /// Get the human readable name for a piece table from its internal name. + /// + /// + /// + /// The matched human readable name or the unchanged internalName if no match was found. + /// + public static string GetDisplayName(string internalName) + { + return NamesMap.FirstOrDefault(x => x.Value == internalName).Key ?? internalName; + } + private static readonly Dictionary NamesMap = new Dictionary { { nameof(Hammer), Hammer }, diff --git a/JotunnLib/Entities/CustomPiece.cs b/JotunnLib/Entities/CustomPiece.cs index 66d2131c4..7c0c9e3f9 100644 --- a/JotunnLib/Entities/CustomPiece.cs +++ b/JotunnLib/Entities/CustomPiece.cs @@ -27,7 +27,21 @@ public class CustomPiece : CustomEntity /// /// Name of the this custom piece belongs to. /// - public string PieceTable { get; set; } + public string PieceTable + { + get => pieceTable; + set + { + var oldPieceTable = pieceTable; + pieceTable = value; + + if (Piece && !string.IsNullOrEmpty(pieceTable)) + { + PieceManager.Instance.RemoveFromPieceTable(Piece, oldPieceTable); + PieceManager.Instance.AddToPieceTable(Piece, pieceTable); + } + } + } /// /// Name of the category this custom piece belongs to.
@@ -56,6 +70,8 @@ public string Category public Setting CategorySetting { get; set; } + public Setting PieceTableSetting { get; set; } + /// /// Indicator if references from configs should get replaced /// @@ -69,6 +85,7 @@ private string PieceName private string fallbackPieceName; private string category; + private string pieceTable; /// /// Custom piece from a prefab.
@@ -109,7 +126,7 @@ public CustomPiece(GameObject piecePrefab, PieceConfig pieceConfig) : base(Assem pieceConfig.Apply(piecePrefab); CreateSettings(); } - + /// /// Custom piece from a prefab with a attached.
/// The members and references from the will be referenced by Jötunn at runtime. @@ -185,7 +202,7 @@ public CustomPiece(AssetBundle assetBundle, string assetName, PieceConfig pieceC pieceConfig.Apply(PiecePrefab); CreateSettings(); } - + /// /// Custom piece from a prefab loaded from an with a attached.
/// The members and references from the will be referenced by Jötunn at runtime. @@ -319,15 +336,18 @@ public CustomPiece(string name, string baseName, PieceConfig pieceConfig) : base private void CreateSettings() { - SettingsEnabled = new BepInExSetting(SourceMod, PiecePrefab.name, "Enabled", false, "Enable this custom piece", 10); + SettingsEnabled = new BepInExSetting(SourceMod, PiecePrefab.name, "Enabled", false, $"Enable settings for {PiecePrefab.name}", 10); SettingsEnabled.OnChanged += () => { BindSettings(); ConfigManagerUtils.BuildSettingList(); }; - CategorySetting = new BepInExSetting(SourceMod, PiecePrefab.name, "Category", Category, "Category of this custom piece", 9); + CategorySetting = new BepInExSetting(SourceMod, PiecePrefab.name, "Category", Category, $"Tool Category of {PiecePrefab.name}", 9); CategorySetting.OnChanged += () => Category = CategorySetting.Value; + + PieceTableSetting = new BepInExSetting(SourceMod, PiecePrefab.name, "Tool", PieceTables.GetDisplayName(PieceTable), $"Tool of of {PiecePrefab.name}", 8); + PieceTableSetting.OnChanged += () => PieceTable = PieceTableSetting.Value; } /// @@ -344,20 +364,24 @@ public bool IsValid() Logger.LogError(SourceMod, $"CustomPiece '{this}' has no prefab"); valid = false; } + if (PiecePrefab && !PiecePrefab.IsValid()) { valid = false; } + if (!Piece) { Logger.LogError(SourceMod, $"CustomPiece '{this}' has no Piece component"); valid = false; } + if (Piece && !Piece.m_icon) { Logger.LogError(SourceMod, $"CustomPiece '{this}' has no icon"); valid = false; } + if (string.IsNullOrEmpty(PieceTable)) { Logger.LogError(SourceMod, $"CustomPiece '{this}' has no PieceTable"); @@ -371,6 +395,7 @@ public void BindSettings() { SettingsEnabled?.Bind(); CategorySetting?.UpdateBinding(SettingsEnabled?.Value ?? false); + PieceTableSetting?.UpdateBinding(SettingsEnabled?.Value ?? false); } /// diff --git a/JotunnLib/Managers/PieceManager.cs b/JotunnLib/Managers/PieceManager.cs index 831e5f922..beb4c976f 100644 --- a/JotunnLib/Managers/PieceManager.cs +++ b/JotunnLib/Managers/PieceManager.cs @@ -256,6 +256,56 @@ public PieceTable GetPieceTable(string name) return null; } + /// + /// Add a to a by name. + /// + /// + /// + /// true if the piece was added to the table + public bool AddToPieceTable(Piece piece, string table) + { + if (!piece || string.IsNullOrEmpty(table)) + { + return false; + } + + var pieceTable = GetPieceTable(table); + if (!pieceTable) + { + return false; + } + + if (pieceTable.m_pieces.Contains(piece.gameObject)) + { + return false; + } + + pieceTable.m_pieces.Add(piece.gameObject); + return true; + } + + /// + /// Remove a from a by name. + /// + /// + /// + /// true if the piece was removed from the table + public bool RemoveFromPieceTable(Piece piece, string table) + { + if (!piece || string.IsNullOrEmpty(table)) + { + return false; + } + + var pieceTable = GetPieceTable(table); + if (!pieceTable) + { + return false; + } + + return pieceTable.m_pieces.Remove(piece.gameObject); + } + /// /// Returns all instances in the game. /// The list is gathered on every ObjectDB.Awake() from all items in it, @@ -603,18 +653,6 @@ private void RegisterPieceInPieceTable(GameObject prefab, string pieceTable, str throw new Exception($"Prefab {prefab.name} has no Piece component attached"); } - var table = GetPieceTable(pieceTable); - if (table == null) - { - throw new Exception($"Could not find PieceTable {pieceTable}"); - } - - if (table.m_pieces.Contains(prefab)) - { - Logger.LogDebug($"Already added piece {prefab.name}"); - return; - } - var name = prefab.name; var hash = name.GetStableHashCode(); @@ -628,13 +666,18 @@ private void RegisterPieceInPieceTable(GameObject prefab, string pieceTable, str PrefabManager.Instance.RegisterToZNetScene(prefab); } + if (!AddToPieceTable(piece, pieceTable)) + { + if (!GetPieceTable(pieceTable)) + { + Logger.LogWarning($"Could not find PieceTable {pieceTable}"); + } + } + if (!string.IsNullOrEmpty(category)) { piece.m_category = AddPieceCategory(category); } - - table.m_pieces.Add(prefab); - Logger.LogDebug($"Added piece {prefab.name} | Token: {piece.TokenName()}"); } private void RegisterCustomData(ObjectDB self) From 786e4e8b2f1a4391d2ec69f69b89ec71833edbbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Wed, 31 Jul 2024 21:41:18 +0200 Subject: [PATCH 3/4] feat: added BepInExDropdownSetting --- JotunnLib/Entities/CustomPiece.cs | 4 +- JotunnLib/Settings/BepInExDropdownSetting.cs | 83 ++++++++++ JotunnLib/Settings/BepInExSetting.cs | 18 ++- JotunnLib/Settings/ComboBox.cs | 142 ++++++++++++++++++ .../Utils/ConfigurationManagerAttributes.cs | 2 + 5 files changed, 239 insertions(+), 10 deletions(-) create mode 100644 JotunnLib/Settings/BepInExDropdownSetting.cs create mode 100644 JotunnLib/Settings/ComboBox.cs diff --git a/JotunnLib/Entities/CustomPiece.cs b/JotunnLib/Entities/CustomPiece.cs index 7c0c9e3f9..b289cd2bc 100644 --- a/JotunnLib/Entities/CustomPiece.cs +++ b/JotunnLib/Entities/CustomPiece.cs @@ -343,10 +343,10 @@ private void CreateSettings() ConfigManagerUtils.BuildSettingList(); }; - CategorySetting = new BepInExSetting(SourceMod, PiecePrefab.name, "Category", Category, $"Tool Category of {PiecePrefab.name}", 9); + CategorySetting = new BepInExDropdownSetting(SourceMod, PiecePrefab.name, "Category", Category, PieceCategories.GetNames().Keys, $"Tool Category of {PiecePrefab.name}", 9); CategorySetting.OnChanged += () => Category = CategorySetting.Value; - PieceTableSetting = new BepInExSetting(SourceMod, PiecePrefab.name, "Tool", PieceTables.GetDisplayName(PieceTable), $"Tool of of {PiecePrefab.name}", 8); + PieceTableSetting = new BepInExDropdownSetting(SourceMod, PiecePrefab.name, "Tool", PieceTables.GetDisplayName(PieceTable), PieceTables.GetNames().Keys, $"Tool of of {PiecePrefab.name}", 8); PieceTableSetting.OnChanged += () => PieceTable = PieceTableSetting.Value; } diff --git a/JotunnLib/Settings/BepInExDropdownSetting.cs b/JotunnLib/Settings/BepInExDropdownSetting.cs new file mode 100644 index 000000000..441f508e2 --- /dev/null +++ b/JotunnLib/Settings/BepInExDropdownSetting.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Reflection; +using BepInEx; +using HarmonyLib; +using Jotunn.Utils; +using UnityEngine; + +namespace Jotunn.Settings +{ + public class BepInExDropdownSetting : BepInExSetting + { + private List values; + + private static Dictionary comboboxes = new Dictionary(); + + private static GUIStyle dropDownStyle; + private static GUIStyle listStyle; + + private static PropertyInfo SettingWindowRect { get; } + + static BepInExDropdownSetting() + { + var configManagerType = AccessTools.TypeByName("ConfigurationManager.ConfigurationManager, ConfigurationManager"); + SettingWindowRect = AccessTools.Property(configManagerType, "SettingWindowRect"); + } + + public BepInExDropdownSetting(BepInPlugin sourceMod, string section, string key, T defaultValue, IEnumerable values, string description, int order, bool adminOnly = true) : base(sourceMod, section, key, defaultValue, description, order, adminOnly) + { + this.values = new List(values); + } + + protected override ConfigurationManagerAttributes GenerateAttributes() + { + ConfigurationManagerAttributes attributes = base.GenerateAttributes(); + attributes.CustomDrawer = Drawer; + attributes.autoCompleteList = values; + return attributes; + } + + protected virtual void Drawer(BepInEx.Configuration.ConfigEntryBase entry) + { + if (dropDownStyle == null) + { + dropDownStyle = new GUIStyle(UnityEngine.GUI.skin.button); + dropDownStyle.clipping = TextClipping.Overflow; + dropDownStyle.alignment = TextAnchor.MiddleCenter; + } + + if (listStyle == null) + { + listStyle = new GUIStyle(UnityEngine.GUI.skin.button); + listStyle.clipping = TextClipping.Overflow; + } + + entry.BoxedValue = GUILayout.TextField(entry.BoxedValue.ToString(), GUILayout.ExpandWidth(true)); + + var buttonText = new GUIContent("\u25bc"); + var dispRect = GUILayoutUtility.GetRect(buttonText, dropDownStyle, GUILayout.Width(25)); + + if (!comboboxes.TryGetValue(entry, out ComboBox combobox)) + { + var attributes = entry.GetConfigurationManagerAttributes(); + var contents = ((List)attributes.autoCompleteList).ConvertAll(x => new GUIContent(x.ToString())).ToArray(); + + var settingWindowRect = (Rect)SettingWindowRect.GetValue(ConfigManagerUtils.Plugin); + + combobox = new ComboBox(dispRect, buttonText, contents, listStyle, dropDownStyle, settingWindowRect.yMax); + comboboxes.Add(entry, combobox); + } + else + { + combobox.Rect = dispRect; + combobox.ButtonContent = buttonText; + } + + combobox.Show(index => + { + var attributes = entry.GetConfigurationManagerAttributes(); + entry.BoxedValue = ((List)attributes.autoCompleteList)[index]; + }); + } + } +} diff --git a/JotunnLib/Settings/BepInExSetting.cs b/JotunnLib/Settings/BepInExSetting.cs index 2a0bf77eb..d5bdeed1c 100644 --- a/JotunnLib/Settings/BepInExSetting.cs +++ b/JotunnLib/Settings/BepInExSetting.cs @@ -14,7 +14,6 @@ public class BepInExSetting : Setting public bool AdminOnly { get; set; } private ConfigEntry entry; - private ConfigurationManagerAttributes attributes; public BepInExSetting(BepInPlugin sourceMod, string section, string key, T defaultValue, string description, int order, bool adminOnly = true) : base(sourceMod) { @@ -34,13 +33,7 @@ public override void Bind() } BaseUnityPlugin plugin = Chainloader.PluginInfos[SourceMod.GUID].Instance; - attributes = new ConfigurationManagerAttributes - { - IsAdminOnly = AdminOnly, - Order = Order, - }; - - entry = plugin.Config.Bind(Section, Key, DefaultValue, new ConfigDescription(Description, null, attributes)); + entry = plugin.Config.Bind(Section, Key, DefaultValue, new ConfigDescription(Description, null, GenerateAttributes())); entry.SettingChanged += (sender, args) => Value = entry.Value; Value = entry.Value; } @@ -63,5 +56,14 @@ public override void Unbind() Value = DefaultValue; } + + protected virtual ConfigurationManagerAttributes GenerateAttributes() + { + return new ConfigurationManagerAttributes + { + IsAdminOnly = AdminOnly, + Order = Order, + }; + } } } diff --git a/JotunnLib/Settings/ComboBox.cs b/JotunnLib/Settings/ComboBox.cs new file mode 100644 index 000000000..90c237047 --- /dev/null +++ b/JotunnLib/Settings/ComboBox.cs @@ -0,0 +1,142 @@ +// Adjusted code from BepInEx.ConfigurationManager: +// https://github.com/BepInEx/BepInEx.ConfigurationManager/blob/master/ConfigurationManager.Shared/Utilities/ComboBox.cs + +using System; +using System.Reflection; +using HarmonyLib; +using UnityEngine; + +namespace Jotunn.Settings +{ + internal class ComboBox + { + private static FieldInfo forceToUnShow; + private static FieldInfo useControlID; + + private bool isClickedComboButton; + private readonly GUIContent[] listContent; + private readonly GUIStyle listStyle; + private readonly GUIStyle dropDownStyle; + private readonly int _windowYmax; + + private static MethodInfo DrawContolBackground { get; } + + private static PropertyInfo CurrentDropdownDrawer { get; } + + static ComboBox() + { + var imageUtilsType = AccessTools.TypeByName("ConfigurationManager.Utilities.ImguiUtils, ConfigurationManager"); + DrawContolBackground = AccessTools.Method(imageUtilsType, "DrawContolBackground", new Type[] { typeof(Rect), typeof(Color) }); + + var bepComboBoxType = AccessTools.TypeByName("ConfigurationManager.Utilities.ComboBox, ConfigurationManager"); + forceToUnShow = AccessTools.Field(bepComboBoxType, "forceToUnShow"); + useControlID = AccessTools.Field(bepComboBoxType, "useControlID"); + CurrentDropdownDrawer = AccessTools.Property(bepComboBoxType, "CurrentDropdownDrawer"); + } + + public ComboBox(Rect rect, GUIContent buttonContent, GUIContent[] listContent, GUIStyle listStyle, GUIStyle dropDownStyle, float windowYmax) + { + Rect = rect; + ButtonContent = buttonContent; + this.listContent = listContent; + this.listStyle = listStyle; + this.dropDownStyle = dropDownStyle; + _windowYmax = (int)windowYmax; + } + + public Rect Rect { get; set; } + + public GUIContent ButtonContent { get; set; } + + public void Show(Action onItemSelected) + { + if ((bool)forceToUnShow.GetValue(null)) + { + forceToUnShow.SetValue(null, false); + isClickedComboButton = false; + } + + var done = false; + var controlID = GUIUtility.GetControlID(FocusType.Passive); + + Vector2 currentMousePosition = Vector2.zero; + if (Event.current.GetTypeForControl(controlID) == EventType.MouseUp) + { + if (isClickedComboButton) + { + done = true; + currentMousePosition = Event.current.mousePosition; + } + } + + if (UnityEngine.GUI.Button(Rect, ButtonContent, dropDownStyle)) + { + if ((int)useControlID.GetValue(null) == -1) + { + useControlID.SetValue(null, controlID); + isClickedComboButton = false; + } + + if ((int)useControlID.GetValue(null) != controlID) + { + forceToUnShow.SetValue(null, true); + useControlID.SetValue(null, controlID); + } + + isClickedComboButton = true; + } + + if (isClickedComboButton) + { + UnityEngine.GUI.enabled = false; + UnityEngine.GUI.color = new Color(1, 1, 1, 2); + + var listRect = new Rect(Rect.x - 150f, Rect.y, Rect.width + 150f, Rect.height); + + var location = GUIUtility.GUIToScreenPoint(new Vector2(listRect.x, listRect.y + listStyle.CalcHeight(listContent[0], 1.0f))); + var size = new Vector2(listRect.width, listStyle.CalcHeight(listContent[0], 1.0f) * listContent.Length); + + var innerRect = new Rect(0, 0, size.x, size.y); + + var outerRectScreen = new Rect(location.x, location.y, size.x, size.y); + if (outerRectScreen.yMax > _windowYmax) + { + outerRectScreen.height = _windowYmax - outerRectScreen.y; + outerRectScreen.width += 20; + } + + if (currentMousePosition != Vector2.zero && outerRectScreen.Contains(GUIUtility.GUIToScreenPoint(currentMousePosition))) + done = false; + + Action Drawer = () => + { + UnityEngine.GUI.enabled = true; + + var scrpos = GUIUtility.ScreenToGUIPoint(location); + var outerRectLocal = new Rect(scrpos.x, scrpos.y, outerRectScreen.width, outerRectScreen.height); + + DrawContolBackground.Invoke(null, new object[] { outerRectLocal, default(Color) }); + + _scrollPosition = UnityEngine.GUI.BeginScrollView(outerRectLocal, _scrollPosition, innerRect, false, false); + { + const int initialSelectedItem = -1; + var newSelectedItemIndex = UnityEngine.GUI.SelectionGrid(innerRect, initialSelectedItem, listContent, 1, listStyle); + if (newSelectedItemIndex != initialSelectedItem) + { + onItemSelected(newSelectedItemIndex); + isClickedComboButton = false; + } + } + UnityEngine.GUI.EndScrollView(true); + }; + + CurrentDropdownDrawer.SetValue(null, Drawer); + } + + if (done) + isClickedComboButton = false; + } + + private Vector2 _scrollPosition = Vector2.zero; + } +} diff --git a/JotunnLib/Utils/ConfigurationManagerAttributes.cs b/JotunnLib/Utils/ConfigurationManagerAttributes.cs index 4c5654bce..adb93d61b 100644 --- a/JotunnLib/Utils/ConfigurationManagerAttributes.cs +++ b/JotunnLib/Utils/ConfigurationManagerAttributes.cs @@ -156,6 +156,8 @@ internal set } } + internal object autoCompleteList; + private bool isUnlocked; private static readonly PropertyInfo[] _myProperties = typeof(ConfigurationManagerAttributes).GetProperties(BindingFlags.Instance | BindingFlags.Public); From ba479581c153a64e8e829f207d54df8fcda83fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Sun, 29 Sep 2024 14:20:23 +0200 Subject: [PATCH 4/4] fix: access to Config Manager SettingWindowRect --- JotunnLib/Settings/BepInExDropdownSetting.cs | 11 +---------- JotunnLib/Utils/ConfigManagerUtils.cs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/JotunnLib/Settings/BepInExDropdownSetting.cs b/JotunnLib/Settings/BepInExDropdownSetting.cs index 441f508e2..864e57c16 100644 --- a/JotunnLib/Settings/BepInExDropdownSetting.cs +++ b/JotunnLib/Settings/BepInExDropdownSetting.cs @@ -16,14 +16,6 @@ public class BepInExDropdownSetting : BepInExSetting private static GUIStyle dropDownStyle; private static GUIStyle listStyle; - private static PropertyInfo SettingWindowRect { get; } - - static BepInExDropdownSetting() - { - var configManagerType = AccessTools.TypeByName("ConfigurationManager.ConfigurationManager, ConfigurationManager"); - SettingWindowRect = AccessTools.Property(configManagerType, "SettingWindowRect"); - } - public BepInExDropdownSetting(BepInPlugin sourceMod, string section, string key, T defaultValue, IEnumerable values, string description, int order, bool adminOnly = true) : base(sourceMod, section, key, defaultValue, description, order, adminOnly) { this.values = new List(values); @@ -61,8 +53,7 @@ protected virtual void Drawer(BepInEx.Configuration.ConfigEntryBase entry) { var attributes = entry.GetConfigurationManagerAttributes(); var contents = ((List)attributes.autoCompleteList).ConvertAll(x => new GUIContent(x.ToString())).ToArray(); - - var settingWindowRect = (Rect)SettingWindowRect.GetValue(ConfigManagerUtils.Plugin); + var settingWindowRect = ConfigManagerUtils.SettingWindowRect; combobox = new ComboBox(dispRect, buttonText, contents, listStyle, dropDownStyle, settingWindowRect.yMax); comboboxes.Add(entry, combobox); diff --git a/JotunnLib/Utils/ConfigManagerUtils.cs b/JotunnLib/Utils/ConfigManagerUtils.cs index f6b5d10bd..a597790c0 100644 --- a/JotunnLib/Utils/ConfigManagerUtils.cs +++ b/JotunnLib/Utils/ConfigManagerUtils.cs @@ -3,6 +3,7 @@ using BepInEx; using BepInEx.Bootstrap; using HarmonyLib; +using UnityEngine; namespace Jotunn.Utils { @@ -18,6 +19,7 @@ public static class ConfigManagerUtils private static PropertyInfo displayingWindowInfo; private static MethodInfo buildSettingListMethodInfo; + private static PropertyInfo settingWindowRect; static ConfigManagerUtils() { @@ -26,6 +28,7 @@ static ConfigManagerUtils() Plugin = configManagerInfo.Instance; displayingWindowInfo = AccessTools.Property(Plugin.GetType(), "DisplayingWindow"); buildSettingListMethodInfo = AccessTools.Method(Plugin.GetType(), "BuildSettingList"); + settingWindowRect = AccessTools.Property(Plugin.GetType(), "SettingWindowRect"); } } @@ -39,6 +42,15 @@ public static bool DisplayingWindow set => displayingWindowInfo?.SetValue(Plugin, value); } + /// + /// The current rect of the config manager window
+ /// Safe to use even if ConfigurationManager is not installed, will return Rect.zero if not installed. + ///
+ public static Rect SettingWindowRect + { + get => Plugin ? (Rect)settingWindowRect.GetValue(Plugin) : Rect.zero; + } + /// /// Rebuild the setting list. Use to update the config manager window if config settings were removed or added while it was open.
/// Safe to call even if ConfigurationManager is not installed.