From cb12faeb6523c943e0b48ed0402197e91bba96c9 Mon Sep 17 00:00:00 2001 From: myplmy Date: Sun, 17 Aug 2025 03:49:45 +0900 Subject: [PATCH 1/3] enhancement : Add search feature in mod manager RT_Dialog_ListingWithTuple.cs * add new feature : SearchBoxUI - now RT_Dialog_ListingWithTuple() has new argument: bool searchBoxEnabled (default value: false) - Add 5 properties to RT_Dialog_ListingWithTuple Class - Most of the methods that affect dialog drawing have been modified. ( DoWindowContents(), FillMainRect(), DrawCustomRow(), ShowFloatMenu() ) * Add new method : SetDisplayVariables() - This refreshes the arrays (displayKeys, displayValueString, displayKeysOrgIndex) that control displayed rows based on the results from the search box. ModManager.cs * Modified the code to call RT_Dialog_ListingWithTuple() with searchBoxEnabled = true. --- .../Dialogs/RT_Dialog_ListingWithTuple.cs | 99 ++++++++++++++----- Source/Client/Managers/ModManager.cs | 2 +- 2 files changed, 76 insertions(+), 25 deletions(-) diff --git a/Source/Client/Dialogs/RT_Dialog_ListingWithTuple.cs b/Source/Client/Dialogs/RT_Dialog_ListingWithTuple.cs index 83e4c10f..e2ad1d78 100644 --- a/Source/Client/Dialogs/RT_Dialog_ListingWithTuple.cs +++ b/Source/Client/Dialogs/RT_Dialog_ListingWithTuple.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using UnityEngine; using Verse; +using static Shared.CommonEnumerators; namespace GameClient.Dialogs { @@ -17,17 +19,27 @@ public class RT_Dialog_ListingWithTuple : RT_Dialog_Base public int[] ValueInt { get; private set; } + public bool searchBoxEnabled { get; set; } + public string searchBoxInput { get; set; } + + public string[] displayKeys { get; set; } + public string[] displayValueString { get; set; } + public int[] displayKeysOrgIndex { get; set; } + public static string[] DialogTupleListingResultString { get; private set; } public static int[] DialogTupleListingResultInt { get; private set; } - public RT_Dialog_ListingWithTuple(string title, string description, string[] keys, string[] values, int[] defaultValues = null, Action actionAccept = null) + public RT_Dialog_ListingWithTuple(string title, string description, string[] keys, string[] values, int[] defaultValues = null, Action actionAccept = null, bool searchBoxEnabled = false) { this.Title = title; this.Description = description; this.Keys = keys; + this.displayKeys = keys; this.Values = values; this.OnAccept = actionAccept; + this.searchBoxEnabled = searchBoxEnabled; + this.searchBoxInput = string.Empty; closeOnAccept = false; closeOnCancel = false; @@ -35,16 +47,20 @@ public RT_Dialog_ListingWithTuple(string title, string description, string[] key List strings = new List(); for (int i = 0; i < keys.Length; i++) strings.Add(values[0]); ValueString = strings.ToArray(); + displayValueString = strings.ToArray(); List ints = new List(); for (int i = 0; i < keys.Length; i++) ints.Add(0); ValueInt = ints.ToArray(); + displayKeysOrgIndex = Enumerable.Range(0, Keys.Length).ToArray(); + if (defaultValues != null) { for (int i = 0; i < ValueString.Length; i++) { ValueString[i] = values[defaultValues[i]]; + displayValueString[i] = values[defaultValues[i]]; ValueInt[i] = defaultValues[i]; } } @@ -67,6 +83,16 @@ public override void DoWindowContents(Rect rect) Text.Font = GameFont.Medium; Widgets.DrawLineHorizontal(rect.x, descriptionLineDif2, rect.width); + if (searchBoxEnabled) + { + string tempSearchBoxInput = Widgets.TextField(GetRectForLocation(rect, LongButtonSize, RectLocation.TopLeft), searchBoxInput, 16); + if (tempSearchBoxInput != searchBoxInput) + { + searchBoxInput = tempSearchBoxInput; + SetDisplayVariables(); + } + } + FillMainRect(new Rect(0f, descriptionLineDif2 + 10f, rect.width, rect.height - DefaultButtonSize.y - 85f)); Text.Font = GameFont.Small; @@ -84,44 +110,41 @@ public override void DoWindowContents(Rect rect) private void FillMainRect(Rect mainRect) { - float height = 6f + Keys.Length * 30f; - Rect viewRect = new Rect(0f, 0f, mainRect.width - 16f, height); + float Height = 6f + displayKeys.Length * 30f; + Rect viewRect = new Rect(0f, 0f, mainRect.width - 16f, Height); Widgets.BeginScrollView(mainRect, ref ScrollPosition, viewRect); - float num = 0; - float num2 = ScrollPosition.y - 30f; - float num3 = ScrollPosition.y + mainRect.height; - int num4 = 0; + float currentY = 0; + float visibleStartY = ScrollPosition.y - 30f; + float visibleEndY = ScrollPosition.y + mainRect.height; - for (int i = 0; i < Keys.Length; i++) + for (int rowCount = 0; rowCount < displayKeys.Length; rowCount++) { - if (num > num2 && num < num3) + if (currentY > visibleStartY && currentY < visibleEndY) { - Rect rect = new Rect(0f, num, viewRect.width, 30f); - DrawCustomRow(rect, Keys[i], num4); + Rect rect = new Rect(0f, currentY, viewRect.width, 30f); + DrawCustomRow(rect, displayKeys[rowCount], rowCount); } - - num += 30f; - num4++; + currentY += 30f; } Widgets.EndScrollView(); } - private void DrawCustomRow(Rect rect, string element, int index) + private void DrawCustomRow(Rect rect, string element, int displayindex) { Text.Font = GameFont.Small; Rect fixedRect = new Rect(new Vector2(rect.x, rect.y + 5f), new Vector2(rect.width - 16f, rect.height - 5f)); - if (index % 2 == 0) Widgets.DrawHighlight(fixedRect); + if (displayindex % 2 == 0) Widgets.DrawHighlight(fixedRect); Widgets.Label(fixedRect, element); - string buttonLabel = ValueString[index]; + string buttonLabel = ValueString[displayKeysOrgIndex[displayindex]]; if (Widgets.ButtonText(new Rect(new Vector2(rect.xMax - LongButtonSize.x, rect.yMax - LongButtonSize.y), LongButtonSize), buttonLabel)) { - ShowFloatMenu(index, false); + ShowFloatMenu(displayindex, false); } } - private void ShowFloatMenu(int index, bool globalChange) + private void ShowFloatMenu(int displayindex, bool globalChange) { List list = new List(); @@ -129,16 +152,16 @@ private void ShowFloatMenu(int index, bool globalChange) { Action changeSingleValue = delegate { - ValueString[index] = str; - ValueInt[index] = GetValueFromString(str); + ValueString[displayKeysOrgIndex[displayindex]] = str; + ValueInt[displayKeysOrgIndex[displayindex]] = GetValueFromString(str); }; Action changeAllValues = delegate { - for (int i = 0; i < ValueString.Length; i++) + for (int i = 0; i < displayValueString.Length; i++) { - ValueString[i] = str; - ValueInt[i] = GetValueFromString(ValueString[i]); + ValueString[displayKeysOrgIndex[i]] = str; + ValueInt[displayKeysOrgIndex[i]] = GetValueFromString(ValueString[displayKeysOrgIndex[i]]); } }; @@ -153,5 +176,33 @@ private void ShowFloatMenu(int index, bool globalChange) } private int GetValueFromString(string str) { return Values.FirstIndexOf(fetch => fetch == str); } + + private void SetDisplayVariables() + { + if (string.IsNullOrEmpty(searchBoxInput)) + { + displayKeys = Keys; + displayValueString = ValueString; + displayKeysOrgIndex = Enumerable.Range(0, Keys.Length).ToArray(); + } + else + { + List filteredKeys = new List(); + List filteredValues = new List(); + List filteredKeysOrgIndex = new List(); + for (int i = 0; i < Keys.Length; i++) + { + if (Keys[i].Contains(searchBoxInput, StringComparison.OrdinalIgnoreCase)) + { + filteredKeys.Add(Keys[i]); + filteredValues.Add(ValueString[i]); + filteredKeysOrgIndex.Add(i); + } + } + displayKeys = filteredKeys.ToArray(); + displayValueString = filteredValues.ToArray(); + displayKeysOrgIndex = filteredKeysOrgIndex.ToArray(); + } + } } } \ No newline at end of file diff --git a/Source/Client/Managers/ModManager.cs b/Source/Client/Managers/ModManager.cs index ff11ddfb..ce412a31 100644 --- a/Source/Client/Managers/ModManager.cs +++ b/Source/Client/Managers/ModManager.cs @@ -40,7 +40,7 @@ public static void OpenModManagerMenu(bool isFirstEdit = false) string[] values = new string[] { "Required", "Optional", "Forbidden" }; RT_Dialog_ListingWithTuple dialog = new RT_Dialog_ListingWithTuple("Mod Manager", "Manage mods for the server", - keys, values, null, toDo); + keys, values, null, toDo, true); RT_Dialog_Base.PushNewDialog(dialog); } From 613035eafa48cbda14bab21a4788eedc093ccb7d Mon Sep 17 00:00:00 2001 From: myplmy Date: Wed, 20 Aug 2025 20:03:34 +0900 Subject: [PATCH 2/3] refactoring : Delete unused variable, add comments RT_Dialog_ListingWithTuple.cs * Delete unused variable : displayValues - There's no reason to use (yet) * Add comments for detailed description * Set max length of search box's input to constant value : SEARCHBOXINPUTMAXLENGTH * Change variable name : height -> viewRectHeight --- .../Dialogs/RT_Dialog_ListingWithTuple.cs | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/Source/Client/Dialogs/RT_Dialog_ListingWithTuple.cs b/Source/Client/Dialogs/RT_Dialog_ListingWithTuple.cs index e2ad1d78..d102d139 100644 --- a/Source/Client/Dialogs/RT_Dialog_ListingWithTuple.cs +++ b/Source/Client/Dialogs/RT_Dialog_ListingWithTuple.cs @@ -19,12 +19,19 @@ public class RT_Dialog_ListingWithTuple : RT_Dialog_Base public int[] ValueInt { get; private set; } - public bool searchBoxEnabled { get; set; } - public string searchBoxInput { get; set; } + public bool searchBoxEnabled { get; private set; } - public string[] displayKeys { get; set; } - public string[] displayValueString { get; set; } - public int[] displayKeysOrgIndex { get; set; } + public string searchBoxInput { get; private set; } + + private const int SEARCHBOXINPUTMAXLENGTH = 16; + + public string[] displayKeys { get; private set; } + // This holds the keys to be displayed in the UI. + // It contains filtered keys if the search box has input; otherwise, it mirrors the original Keys list. + + public int[] displayKeysOrgIndex { get; private set; } + // Used for converting an index of the filtered 'displayKeys' array to its original index in the 'Keys' array. + // If the search box is empty, it will contain the original 'Keys' indices. public static string[] DialogTupleListingResultString { get; private set; } @@ -47,7 +54,6 @@ public RT_Dialog_ListingWithTuple(string title, string description, string[] key List strings = new List(); for (int i = 0; i < keys.Length; i++) strings.Add(values[0]); ValueString = strings.ToArray(); - displayValueString = strings.ToArray(); List ints = new List(); for (int i = 0; i < keys.Length; i++) ints.Add(0); @@ -60,7 +66,6 @@ public RT_Dialog_ListingWithTuple(string title, string description, string[] key for (int i = 0; i < ValueString.Length; i++) { ValueString[i] = values[defaultValues[i]]; - displayValueString[i] = values[defaultValues[i]]; ValueInt[i] = defaultValues[i]; } } @@ -85,9 +90,11 @@ public override void DoWindowContents(Rect rect) if (searchBoxEnabled) { - string tempSearchBoxInput = Widgets.TextField(GetRectForLocation(rect, LongButtonSize, RectLocation.TopLeft), searchBoxInput, 16); + //Adds a search box to the top-left corner of the dialog when 'searchBoxEnabled' is true. + string tempSearchBoxInput = Widgets.TextField(GetRectForLocation(rect, LongButtonSize, RectLocation.TopLeft), searchBoxInput, SEARCHBOXINPUTMAXLENGTH); if (tempSearchBoxInput != searchBoxInput) { + // Updates the 'searchBoxInput' only if the user has changed it. searchBoxInput = tempSearchBoxInput; SetDisplayVariables(); } @@ -110,8 +117,8 @@ public override void DoWindowContents(Rect rect) private void FillMainRect(Rect mainRect) { - float Height = 6f + displayKeys.Length * 30f; - Rect viewRect = new Rect(0f, 0f, mainRect.width - 16f, Height); + float viewRectHeight = 6f + displayKeys.Length * 30f; + Rect viewRect = new Rect(0f, 0f, mainRect.width - 16f, viewRectHeight); Widgets.BeginScrollView(mainRect, ref ScrollPosition, viewRect); float currentY = 0; float visibleStartY = ScrollPosition.y - 30f; @@ -154,12 +161,16 @@ private void ShowFloatMenu(int displayindex, bool globalChange) { ValueString[displayKeysOrgIndex[displayindex]] = str; ValueInt[displayKeysOrgIndex[displayindex]] = GetValueFromString(str); + // Changes the ValueString & ValueInt of the corresponding row upon clicking the Widgets.ButtonText's FloatMenuOption. + // displayKeysOrgIndex[displayindex]: Gets the original index of a displayed key (from before filtering). }; Action changeAllValues = delegate { - for (int i = 0; i < displayValueString.Length; i++) + for (int i = 0; i < displayKeys.Length; i++) { + // It affects only visible rows/keys (filtered ones), not all keys. + // If the search box is empty, it affects all keys. ValueString[displayKeysOrgIndex[i]] = str; ValueInt[displayKeysOrgIndex[i]] = GetValueFromString(ValueString[displayKeysOrgIndex[i]]); } @@ -179,28 +190,29 @@ private void ShowFloatMenu(int displayindex, bool globalChange) private void SetDisplayVariables() { + // Updates display arrays ('displayKeys' and 'displayKeyOrgIndex') based on the search box input. + // If the search box is empty, it mirrors the 'Keys' array and its original indices. + // Otherwise, it sets them to the values filtered by the searchBoxInput if (string.IsNullOrEmpty(searchBoxInput)) { displayKeys = Keys; - displayValueString = ValueString; displayKeysOrgIndex = Enumerable.Range(0, Keys.Length).ToArray(); } else { + // Filter items based on search input List filteredKeys = new List(); - List filteredValues = new List(); List filteredKeysOrgIndex = new List(); for (int i = 0; i < Keys.Length; i++) { + // Case-insensitive search if (Keys[i].Contains(searchBoxInput, StringComparison.OrdinalIgnoreCase)) { filteredKeys.Add(Keys[i]); - filteredValues.Add(ValueString[i]); filteredKeysOrgIndex.Add(i); } } displayKeys = filteredKeys.ToArray(); - displayValueString = filteredValues.ToArray(); displayKeysOrgIndex = filteredKeysOrgIndex.ToArray(); } } From 454333148d21ba8a5b1dc51a70421ff3d6c0a777 Mon Sep 17 00:00:00 2001 From: myplmy Date: Wed, 20 Aug 2025 20:38:31 +0900 Subject: [PATCH 3/3] refactoring: Consolidate loops, remove namespace for debug RT_Dialog_ListingWithTuple.cs * Refactor initialization of ValueString, ValueInt, and displayKeysOrgIndex in RT_Dialog_ListingWithTuple() * Remove namespace : Shared.CommonEnumerators --- .../Client/Dialogs/RT_Dialog_ListingWithTuple.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/Client/Dialogs/RT_Dialog_ListingWithTuple.cs b/Source/Client/Dialogs/RT_Dialog_ListingWithTuple.cs index d102d139..176dbafc 100644 --- a/Source/Client/Dialogs/RT_Dialog_ListingWithTuple.cs +++ b/Source/Client/Dialogs/RT_Dialog_ListingWithTuple.cs @@ -3,7 +3,6 @@ using System.Linq; using UnityEngine; using Verse; -using static Shared.CommonEnumerators; namespace GameClient.Dialogs { @@ -52,14 +51,17 @@ public RT_Dialog_ListingWithTuple(string title, string description, string[] key closeOnCancel = false; List strings = new List(); - for (int i = 0; i < keys.Length; i++) strings.Add(values[0]); - ValueString = strings.ToArray(); - List ints = new List(); - for (int i = 0; i < keys.Length; i++) ints.Add(0); + List orgIndex = new List(); + for (int i = 0; i < keys.Length; i++) + { + strings.Add(values[0]); + ints.Add(0); + orgIndex.Add(i); + } + ValueString = strings.ToArray(); ValueInt = ints.ToArray(); - - displayKeysOrgIndex = Enumerable.Range(0, Keys.Length).ToArray(); + displayKeysOrgIndex = orgIndex.ToArray(); if (defaultValues != null) {