Added search function, fixed some variable typing

This commit is contained in:
Matt Neumann 2025-03-11 19:43:44 -07:00
parent 8dc217116f
commit 61919ba9a3
3 changed files with 273 additions and 29 deletions

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Globalization;
namespace Better_NCP_Editor
{
@ -188,21 +189,14 @@ namespace Better_NCP_Editor
private void BtnOk_Click(object sender, EventArgs e)
{
// Determine the new value based on the type of the input control.
if (inputControl is ComboBox combo)
{
if (valueType == typeof(bool))
{
// Parse the selected string to a boolean.
if (bool.TryParse(combo.SelectedItem?.ToString(), out bool boolVal))
{
NewValue = boolVal;
}
else
{
// Handle parsing error as needed.
NewValue = false;
}
}
else
{
@ -212,11 +206,32 @@ namespace Better_NCP_Editor
}
else if (inputControl is TextBox txt)
{
// If possible, parse to int; otherwise, leave as string.
if (int.TryParse(txt.Text, out int intValue))
// Parse the value based on the expected type using invariant culture.
if (valueType == typeof(int) &&
int.TryParse(txt.Text, NumberStyles.Integer, CultureInfo.InvariantCulture, out int intValue))
{
NewValue = intValue;
}
else if (valueType == typeof(double) &&
double.TryParse(txt.Text, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out double doubleValue))
{
NewValue = doubleValue;
}
else if (valueType == typeof(float) &&
float.TryParse(txt.Text, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out float floatValue))
{
NewValue = floatValue;
}
else if (valueType == typeof(bool) &&
bool.TryParse(txt.Text, out bool boolValue))
{
NewValue = boolValue;
}
else
{
// Default to string if parsing fails.
NewValue = txt.Text;
}
}
this.DialogResult = DialogResult.OK;
this.Close();

View File

@ -39,6 +39,9 @@
btn_import_entityData = new Button();
btn_export_entityData = new Button();
statusTextbox = new TextBox();
searchTextBox = new TextBox();
btn_Search = new Button();
btn_Search_Clear = new Button();
SuspendLayout();
//
// btn_load
@ -69,7 +72,7 @@
dirTreeView.Location = new Point(15, 40);
dirTreeView.Margin = new Padding(3, 2, 3, 2);
dirTreeView.Name = "dirTreeView";
dirTreeView.Size = new Size(254, 610);
dirTreeView.Size = new Size(254, 623);
dirTreeView.TabIndex = 2;
//
// entityTreeView
@ -77,7 +80,7 @@
entityTreeView.Location = new Point(274, 40);
entityTreeView.Margin = new Padding(3, 2, 3, 2);
entityTreeView.Name = "entityTreeView";
entityTreeView.Size = new Size(738, 610);
entityTreeView.Size = new Size(738, 623);
entityTreeView.TabIndex = 3;
//
// btn_entity_del
@ -107,10 +110,10 @@
// btn_import_entityData
//
btn_import_entityData.Enabled = false;
btn_import_entityData.Location = new Point(431, 9);
btn_import_entityData.Location = new Point(366, 9);
btn_import_entityData.Margin = new Padding(3, 2, 3, 2);
btn_import_entityData.Name = "btn_import_entityData";
btn_import_entityData.Size = new Size(61, 22);
btn_import_entityData.Size = new Size(68, 22);
btn_import_entityData.TabIndex = 9;
btn_import_entityData.Text = "Import";
btn_import_entityData.UseVisualStyleBackColor = true;
@ -119,10 +122,10 @@
// btn_export_entityData
//
btn_export_entityData.Enabled = false;
btn_export_entityData.Location = new Point(498, 9);
btn_export_entityData.Location = new Point(440, 9);
btn_export_entityData.Margin = new Padding(3, 2, 3, 2);
btn_export_entityData.Name = "btn_export_entityData";
btn_export_entityData.Size = new Size(58, 22);
btn_export_entityData.Size = new Size(65, 22);
btn_export_entityData.TabIndex = 10;
btn_export_entityData.Text = "Export";
btn_export_entityData.UseVisualStyleBackColor = true;
@ -132,18 +135,50 @@
//
statusTextbox.BorderStyle = BorderStyle.FixedSingle;
statusTextbox.ImeMode = ImeMode.NoControl;
statusTextbox.Location = new Point(578, 10);
statusTextbox.Location = new Point(15, 667);
statusTextbox.Margin = new Padding(3, 2, 3, 2);
statusTextbox.Name = "statusTextbox";
statusTextbox.ReadOnly = true;
statusTextbox.Size = new Size(434, 23);
statusTextbox.Size = new Size(997, 23);
statusTextbox.TabIndex = 11;
//
// searchTextBox
//
searchTextBox.Location = new Point(725, 8);
searchTextBox.Name = "searchTextBox";
searchTextBox.Size = new Size(287, 23);
searchTextBox.TabIndex = 12;
//
// btn_Search
//
btn_Search.Location = new Point(605, 7);
btn_Search.Margin = new Padding(3, 2, 3, 2);
btn_Search.Name = "btn_Search";
btn_Search.Size = new Size(65, 22);
btn_Search.TabIndex = 13;
btn_Search.Text = "Search";
btn_Search.UseVisualStyleBackColor = true;
btn_Search.Click += btn_Search_Click;
//
// btn_Search_Clear
//
btn_Search_Clear.Location = new Point(676, 7);
btn_Search_Clear.Margin = new Padding(3, 2, 3, 2);
btn_Search_Clear.Name = "btn_Search_Clear";
btn_Search_Clear.Size = new Size(43, 22);
btn_Search_Clear.TabIndex = 14;
btn_Search_Clear.Text = "Clear";
btn_Search_Clear.UseVisualStyleBackColor = true;
btn_Search_Clear.Click += btn_Search_Clear_Click;
//
// Form1
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(1028, 661);
ClientSize = new Size(1028, 696);
Controls.Add(btn_Search_Clear);
Controls.Add(btn_Search);
Controls.Add(searchTextBox);
Controls.Add(statusTextbox);
Controls.Add(btn_export_entityData);
Controls.Add(btn_import_entityData);
@ -155,7 +190,7 @@
Controls.Add(btn_load);
Margin = new Padding(3, 2, 3, 2);
Name = "Form1";
Text = "BetterNPC Editor V1.0";
Text = "BetterNPC";
Load += Form1_Load;
ResumeLayout(false);
PerformLayout();
@ -172,5 +207,8 @@
private Button btn_import_entityData;
private Button btn_export_entityData;
private TextBox statusTextbox;
private TextBox searchTextBox;
private Button btn_Search;
private Button btn_Search_Clear;
}
}

View File

@ -5,11 +5,14 @@ using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization.Metadata;
using System.Windows.Forms;
using System.Globalization;
namespace Better_NCP_Editor
{
public partial class Form1 : Form
{
private const string formTitle = "Better NCP Editor";
private const string version = "0.0.2-alpha";
private string currentJsonFilePath;
private JsonNode currentJson;
private bool fileModified = false;
@ -23,7 +26,7 @@ namespace Better_NCP_Editor
private List<String> _beltItems;
private List<String> _weaponModItems;
private Dictionary<String, Dictionary<String,UInt64>> _itemShortnameToSkinName;
private Dictionary<String, Dictionary<String, UInt64>> _itemShortnameToSkinName;
// All items dictionary.
private Dictionary<String, String> _allItems;
@ -31,13 +34,16 @@ namespace Better_NCP_Editor
public Form1()
{
InitializeComponent();
this.Text = $"{formTitle} v{version}";
this.AutoScaleMode = AutoScaleMode.None;
dirTreeView.AfterSelect += DirTreeView_AfterSelect;
entityTreeView.NodeMouseDoubleClick += entityTreeView_NodeMouseDoubleClick;
entityTreeView.AfterSelect += entityTreeView_AfterSelect;
searchTextBox.KeyDown += SearchTextBox_KeyDown;
// Prevent the form from shrinking below its current size.
this.MinimumSize = new Size(1044, 700); // Set minimum allowed size
this.MinimumSize = new Size(1044, 735); // Set minimum allowed size
this.FormBorderStyle = FormBorderStyle.Sizable;
// Example layout using docking:
@ -50,6 +56,7 @@ namespace Better_NCP_Editor
// In the designer or constructor:
dirTreeView.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left;
entityTreeView.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
statusTextbox.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
entityTreeView.ShowNodeToolTips = false;
// Configure the custom tooltip.
@ -87,7 +94,7 @@ namespace Better_NCP_Editor
}
}
private Dictionary<String,String> LoadItemDatabase(String filepath)
private Dictionary<String, String> LoadItemDatabase(String filepath)
{
try
{
@ -101,7 +108,7 @@ namespace Better_NCP_Editor
}
}
private Dictionary<String,Dictionary<String, UInt64>> LoadSkinDict(String filepath)
private Dictionary<String, Dictionary<String, UInt64>> LoadSkinDict(String filepath)
{
try
{
@ -265,9 +272,11 @@ namespace Better_NCP_Editor
foreach (var kvp in obj)
{
TreeNode child;
if (kvp.Value is JsonValue)
if (kvp.Value is JsonValue jsonValue)
{
child = new TreeNode($"{kvp.Key}: {kvp.Value}");
// Format the value properly based on its type
string displayValue = FormatJsonValueForDisplay(jsonValue);
child = new TreeNode($"{kvp.Key}: {displayValue}");
}
else
{
@ -309,9 +318,11 @@ namespace Better_NCP_Editor
{
JsonNode item = arr[i];
TreeNode child;
if (item is JsonValue)
if (item is JsonValue jsonValue)
{
child = new TreeNode($"[{i}]: {item}");
// Format the value properly based on its type
string displayValue = FormatJsonValueForDisplay(jsonValue);
child = new TreeNode($"[{i}]: {displayValue}");
}
else
{
@ -324,6 +335,31 @@ namespace Better_NCP_Editor
}
}
// Helper method to properly format JSON values for display
private string FormatJsonValueForDisplay(JsonValue jsonValue)
{
try
{
// Get the underlying value
object value = jsonValue.GetValue<object>();
if (value is double doubleVal)
return doubleVal.ToString(CultureInfo.InvariantCulture);
else if (value is float floatVal)
return floatVal.ToString(CultureInfo.InvariantCulture);
else if (value is bool boolVal)
return boolVal.ToString().ToLowerInvariant(); // Display as lowercase true/false
else if (value is string str)
return str; // Return the raw string without quotes.
else
return jsonValue.ToString(); // Default fallback.
}
catch
{
return jsonValue.ToString();
}
}
private void entityTreeView_AfterSelect(object sender, TreeViewEventArgs e)
{
@ -429,8 +465,12 @@ namespace Better_NCP_Editor
{
if (bool.TryParse(currentVal, out _))
return typeof(bool);
if (int.TryParse(currentVal, out _))
if (int.TryParse(currentVal, NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
return typeof(int);
if (float.TryParse(currentVal, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out _))
return typeof(float);
if (double.TryParse(currentVal, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out _))
return typeof(double);
return typeof(string);
}
@ -589,7 +629,8 @@ namespace Better_NCP_Editor
obj["Mods"] = new JsonArray(); // Empty array
obj["Ammo"] = "";
newNode = obj;
} else if (arrayName.Equals("List of prefabs", StringComparison.OrdinalIgnoreCase))
}
else if (arrayName.Equals("List of prefabs", StringComparison.OrdinalIgnoreCase))
{
var obj = new JsonObject();
obj["Chance [0.0-100.0]"] = 100.0;
@ -945,5 +986,155 @@ namespace Better_NCP_Editor
}
}
}
private void btn_Search_Click(object sender, EventArgs e)
{
string searchText = searchTextBox.Text.Trim().ToLowerInvariant();
if (string.IsNullOrEmpty(searchText))
{
// If search text is empty, restore the full tree view
if (currentJson != null)
{
PopulateEntityTree(currentJson);
statusTextbox.Text = "Search cleared. Displaying all nodes.";
}
return;
}
if (currentJson == null)
{
statusTextbox.Text = "No JSON file loaded to search.";
return;
}
// Clear the tree view before repopulating with filtered results
entityTreeView.Nodes.Clear();
// Get the filename for the root node
string fileName = string.IsNullOrEmpty(currentJsonFilePath) ? "JSON" : Path.GetFileName(currentJsonFilePath);
TreeNode rootNode = new TreeNode(fileName)
{
Tag = currentJson
};
entityTreeView.Nodes.Add(rootNode);
// Use a flag to track if any matches were found
bool foundMatches = SearchAndPopulateTree(currentJson, rootNode, searchText);
if (foundMatches)
{
rootNode.ExpandAll();
statusTextbox.Text = $"Search complete. Showing results for: '{searchText}'";
}
else
{
// No matches found
PopulateEntityTree(currentJson); // Restore the full tree
statusTextbox.Text = $"No matches found for: '{searchText}'";
}
}
private bool SearchAndPopulateTree(JsonNode node, TreeNode treeNode, string searchText)
{
bool foundMatch = false;
if (node is JsonObject obj)
{
foreach (var kvp in obj)
{
bool keyMatch = kvp.Key.ToLowerInvariant().Contains(searchText);
bool valueMatch = false;
TreeNode child;
if (kvp.Value is JsonValue jsonValue)
{
string displayValue = FormatJsonValueForDisplay(jsonValue);
string valueStr = jsonValue.ToString().ToLowerInvariant();
valueMatch = valueStr.Contains(searchText);
child = new TreeNode($"{kvp.Key}: {displayValue}");
}
else
{
child = new TreeNode(kvp.Key);
}
child.ToolTipText = toolTips.tips.ContainsKey(kvp.Key) ? toolTips.tips[kvp.Key] : "";
child.Tag = kvp.Value;
if (keyMatch || valueMatch)
{
// Direct match found:
// Add the full subtree without filtering.
PopulateTreeRecursive(kvp.Value, child);
treeNode.Nodes.Add(child);
foundMatch = true;
}
else
{
// No direct match: continue filtering the children.
bool childrenMatch = SearchAndPopulateTree(kvp.Value, child, searchText);
if (childrenMatch)
{
treeNode.Nodes.Add(child);
foundMatch = true;
}
}
}
}
else if (node is JsonArray arr)
{
for (int i = 0; i < arr.Count; i++)
{
JsonNode item = arr[i];
TreeNode child;
if (item is JsonValue jsonValue)
{
string displayValue = FormatJsonValueForDisplay(jsonValue);
string valueStr = jsonValue.ToString().ToLowerInvariant();
bool valueMatch = valueStr.Contains(searchText);
child = new TreeNode($"[{i}]: {displayValue}");
if (valueMatch)
{
child.BackColor = Color.LightYellow;
treeNode.Nodes.Add(child);
foundMatch = true;
}
}
else
{
child = new TreeNode($"[{i}]");
child.Tag = item;
bool childrenMatch = SearchAndPopulateTree(item, child, searchText);
if (childrenMatch)
{
treeNode.Nodes.Add(child);
foundMatch = true;
}
}
}
}
return foundMatch;
}
private void btn_Search_Clear_Click(object sender, EventArgs e)
{
searchTextBox.Clear();
btn_Search_Click(sender, e);
}
private void SearchTextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
// Prevent the beep sound
e.SuppressKeyPress = true;
// Trigger the search button click
btn_Search_Click(sender, EventArgs.Empty);
}
}
}
}