From 625acc67c172b1894c98d3e430a4879a339a8433 Mon Sep 17 00:00:00 2001 From: Mikalai Kryvusha Date: Sun, 11 Oct 2026 11:26:02 +0300 Subject: [PATCH] Add WindowSkin: let a separate plugin give the settings window the look of the game WindowSkin (null by default) is used to draw the window, its controls and tooltips; its window style draws the window background instead of the built-in grey one. Plugin and category headers use the skin's custom styles named ConfigurationManager.PluginHeader / ConfigurationManager.CategoryHeader if present. The default look is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../ConfigurationManager.cs | 26 ++++++++++- .../SettingFieldDrawer.cs | 43 +++++++++++-------- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/ConfigurationManager.Shared/ConfigurationManager.cs b/ConfigurationManager.Shared/ConfigurationManager.cs index 251a848..fa0fdc0 100644 --- a/ConfigurationManager.Shared/ConfigurationManager.cs +++ b/ConfigurationManager.Shared/ConfigurationManager.cs @@ -50,6 +50,25 @@ public class ConfigurationManager : BaseUnityPlugin private bool _focusSearchBox; private string _searchString = string.Empty; + /// + /// Skin to draw the settings window with, for example to match the look of the game. Null (the default) keeps the default look. + /// The window, its controls and tooltips are drawn with this skin, and its window style draws the window background. + /// The dimmed backdrop behind a window that has not been moved yet keeps the default skin. + /// Plugin and category headers use the custom styles named and + /// if the skin has them. + /// + public GUISkin WindowSkin { get; set; } + + /// + /// Name of the custom style in used for plugin headers, if present. + /// + public const string PluginHeaderStyleName = "ConfigurationManager.PluginHeader"; + + /// + /// Name of the custom style in used for category headers, if present. + /// + public const string CategoryHeaderStyleName = "ConfigurationManager.CategoryHeader"; + /// /// Event fired every time the manager window is shown or hidden. /// @@ -309,10 +328,15 @@ private void OnGUI() if (GUI.Button(_screenRect, string.Empty, GUI.skin.box) && !SettingWindowRect.Contains(mousePosition)) DisplayingWindow = false; - ImguiUtils.DrawWindowBackground(SettingWindowRect); + // A custom skin draws the window background with its own window style + if (WindowSkin == null) + ImguiUtils.DrawWindowBackground(SettingWindowRect); } + var defaultSkin = GUI.skin; + if (WindowSkin != null) GUI.skin = WindowSkin; var newRect = GUILayout.Window(WindowId, SettingWindowRect, (GUI.WindowFunction)SettingsWindow, "Plugin / mod settings"); + GUI.skin = defaultSkin; if (newRect != SettingWindowRect) { diff --git a/ConfigurationManager.Shared/SettingFieldDrawer.cs b/ConfigurationManager.Shared/SettingFieldDrawer.cs index 1cd81e0..bf7eeb0 100644 --- a/ConfigurationManager.Shared/SettingFieldDrawer.cs +++ b/ConfigurationManager.Shared/SettingFieldDrawer.cs @@ -99,32 +99,41 @@ public static void DrawCenteredLabel(string text, params GUILayoutOption[] optio GUILayout.EndHorizontal(); } - private static GUIStyle _categoryHeaderSkin; - public static void DrawCategoryHeader(string text) + /// + /// Header style of the current skin: its custom style with the given name if it has one, otherwise a centered copy of its label. + /// Rebuilt only when the skin changes, so the default look builds it once as before. + /// + private static GUIStyle GetHeaderStyle(string customStyleName, int fontSize, ref GUIStyle style, ref GUISkin styleSkin) { - if (_categoryHeaderSkin == null) + var skin = GUI.skin; + if (style == null || styleSkin != skin) { - _categoryHeaderSkin = GUI.skin.label.CreateCopy(); - _categoryHeaderSkin.alignment = TextAnchor.UpperCenter; - _categoryHeaderSkin.wordWrap = true; - _categoryHeaderSkin.stretchWidth = true; - _categoryHeaderSkin.fontSize = 14; + style = skin.FindStyle(customStyleName); + if (style == null) + { + style = skin.label.CreateCopy(); + style.alignment = TextAnchor.UpperCenter; + style.wordWrap = true; + style.stretchWidth = true; + style.fontSize = fontSize; + } + styleSkin = skin; } + return style; + } - GUILayout.Label(text, _categoryHeaderSkin); + private static GUIStyle _categoryHeaderSkin; + private static GUISkin _categoryHeaderSkinFor; + public static void DrawCategoryHeader(string text) + { + GUILayout.Label(text, GetHeaderStyle(ConfigurationManager.CategoryHeaderStyleName, 14, ref _categoryHeaderSkin, ref _categoryHeaderSkinFor)); } private static GUIStyle _pluginHeaderSkin; + private static GUISkin _pluginHeaderSkinFor; public static bool DrawPluginHeader(GUIContent content, bool isCollapsed) { - if (_pluginHeaderSkin == null) - { - _pluginHeaderSkin = GUI.skin.label.CreateCopy(); - _pluginHeaderSkin.alignment = TextAnchor.UpperCenter; - _pluginHeaderSkin.wordWrap = true; - _pluginHeaderSkin.stretchWidth = true; - _pluginHeaderSkin.fontSize = 15; - } + GetHeaderStyle(ConfigurationManager.PluginHeaderStyleName, 15, ref _pluginHeaderSkin, ref _pluginHeaderSkinFor); if (isCollapsed) content.text += "\n..."; return GUILayout.Button(content, _pluginHeaderSkin, GUILayout.ExpandWidth(true));