Added Game Search / Filter When Scanning Games Closes #15

- Added a "Game search:" label and text box above the game list in
  SelectDialogForm, allowing users to filter the program/game list
  by name in real-time (case-insensitive substring match).
- Added TextBox theming support to ThemeManager for both dark and
  light modes, ensuring the new search field conforms to the active
  theme.
This commit is contained in:
Frog
2026-05-25 02:18:56 -07:00
parent 39097c27ef
commit fe55efc072
3 changed files with 71 additions and 8 deletions
+26 -2
View File
@@ -33,6 +33,8 @@ namespace CreamInstaller.Forms
saveButton = new Button();
uninstallAllButton = new Button();
selectionTreeView = new CustomTreeView();
filterLabel = new System.Windows.Forms.Label();
filterTextBox = new System.Windows.Forms.TextBox();
groupBox.SuspendLayout();
allCheckBoxFlowPanel.SuspendLayout();
SuspendLayout();
@@ -51,15 +53,33 @@ namespace CreamInstaller.Forms
acceptButton.Text = "OK";
acceptButton.UseVisualStyleBackColor = true;
//
// filterLabel
//
filterLabel.AutoSize = true;
filterLabel.Location = new System.Drawing.Point(12, 17);
filterLabel.Name = "filterLabel";
filterLabel.Size = new System.Drawing.Size(68, 15);
filterLabel.TabIndex = 1008;
filterLabel.Text = "Game search:";
//
// filterTextBox
//
filterTextBox.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
filterTextBox.Location = new System.Drawing.Point(100, 14);
filterTextBox.Name = "filterTextBox";
filterTextBox.Size = new System.Drawing.Size(436, 23);
filterTextBox.TabIndex = 0;
filterTextBox.TextChanged += OnFilterTextChanged;
//
// groupBox
//
groupBox.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
groupBox.Controls.Add(selectionTreeView);
groupBox.Controls.Add(allCheckBoxFlowPanel);
groupBox.Location = new System.Drawing.Point(12, 12);
groupBox.Location = new System.Drawing.Point(12, 43);
groupBox.MinimumSize = new System.Drawing.Size(240, 40);
groupBox.Name = "groupBox";
groupBox.Size = new System.Drawing.Size(524, 225);
groupBox.Size = new System.Drawing.Size(524, 194);
groupBox.TabIndex = 3;
groupBox.TabStop = false;
groupBox.Text = "Choices";
@@ -188,6 +208,8 @@ namespace CreamInstaller.Forms
Controls.Add(cancelButton);
Controls.Add(acceptButton);
Controls.Add(groupBox);
Controls.Add(filterTextBox);
Controls.Add(filterLabel);
FormBorderStyle = FormBorderStyle.FixedSingle;
MaximizeBox = false;
MinimizeBox = false;
@@ -215,5 +237,7 @@ namespace CreamInstaller.Forms
private Button saveButton;
private CheckBox sortCheckBox;
private Button uninstallAllButton;
private System.Windows.Forms.Label filterLabel;
private System.Windows.Forms.TextBox filterTextBox;
}
}
+33 -6
View File
@@ -10,6 +10,7 @@ namespace CreamInstaller.Forms;
internal sealed partial class SelectDialogForm : CustomForm
{
private readonly List<(Platform platform, string id, string name)> selected = new();
private readonly List<(Platform platform, string id, string name, bool alreadySelected)> allChoices = new();
internal SelectDialogForm(IWin32Window owner) : base(owner)
{
@@ -28,12 +29,12 @@ internal sealed partial class SelectDialogForm : CustomForm
allCheckBox.Enabled = false;
acceptButton.Enabled = false;
selectionTreeView.AfterCheck += OnTreeNodeChecked;
foreach ((Platform platform, string id, string name, bool alreadySelected) in potentialChoices)
{
TreeNode node = new() { Tag = platform, Name = id, Text = name, Checked = alreadySelected };
OnTreeNodeChecked(node);
_ = selectionTreeView.Nodes.Add(node);
}
allChoices.Clear();
allChoices.AddRange(potentialChoices);
foreach ((Platform platform, string id, string name, bool alreadySelected) in allChoices)
if (alreadySelected)
selected.Add((platform, id, name));
ApplyFilter();
if (selected.Count < 1)
OnLoad(null, null);
@@ -70,6 +71,32 @@ internal sealed partial class SelectDialogForm : CustomForm
allCheckBox.CheckedChanged += OnAllCheckBoxChanged;
}
private void OnFilterTextChanged(object sender, EventArgs e) => ApplyFilter();
private void ApplyFilter()
{
string filter = filterTextBox.Text.Trim();
selectionTreeView.AfterCheck -= OnTreeNodeChecked;
selectionTreeView.BeginUpdate();
selectionTreeView.Nodes.Clear();
bool hasSelections = selected.Count > 0;
foreach ((Platform platform, string id, string name, bool alreadySelected) in allChoices)
{
if (filter.Length > 0 && name.IndexOf(filter, StringComparison.OrdinalIgnoreCase) < 0)
continue;
bool checkedState = hasSelections
? selected.Any(s => s.platform == platform && s.id == id)
: alreadySelected;
TreeNode node = new() { Tag = platform, Name = id, Text = name, Checked = checkedState };
_ = selectionTreeView.Nodes.Add(node);
}
selectionTreeView.EndUpdate();
selectionTreeView.AfterCheck += OnTreeNodeChecked;
allCheckBox.CheckedChanged -= OnAllCheckBoxChanged;
allCheckBox.Checked = selectionTreeView.Nodes.Count > 0 && selectionTreeView.Nodes.Cast<TreeNode>().All(n => n.Checked);
allCheckBox.CheckedChanged += OnAllCheckBoxChanged;
}
private void OnResize(object s, EventArgs e)
=> Text = TextRenderer.MeasureText(Program.ApplicationName, Font).Width > Size.Width - 100
? Program.ApplicationNameShort
+12
View File
@@ -206,6 +206,13 @@ internal static class ThemeManager
rtb.ForeColor = DarkFore;
break;
// TextBox follows alternate dark background
case TextBox tb:
tb.BackColor = DarkBackAlt;
tb.ForeColor = DarkFore;
tb.BorderStyle = BorderStyle.FixedSingle;
break;
// Layout panels set a consistent background
case TableLayoutPanel tlp:
tlp.BackColor = DarkBack;
@@ -258,6 +265,11 @@ internal static class ThemeManager
rtb.BackColor = LightBack;
rtb.ForeColor = LightFore;
break;
case TextBox tb:
tb.BackColor = LightBackAlt;
tb.ForeColor = LightFore;
tb.BorderStyle = BorderStyle.Fixed3D;
break;
case TableLayoutPanel tlp:
tlp.BackColor = LightBack;
break;