From b7067c2621cadf096e6ca9650944dd9360af2c93 Mon Sep 17 00:00:00 2001 From: Frog Date: Mon, 25 May 2026 02:35:50 -0700 Subject: [PATCH] Change Game Filter Text to PlaceHolder Text - Removed the game search label in favor of textbox placeholder text. - Added methods to ensure the placeholder text renders properly - Added inline comments to explain what NativeMethods is for --- .../Forms/SelectDialogForm.Designer.cs | 17 ++------ CreamInstaller/Utility/ThemeManager.cs | 41 +++++++++++++++++++ 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/CreamInstaller/Forms/SelectDialogForm.Designer.cs b/CreamInstaller/Forms/SelectDialogForm.Designer.cs index c627b86..4df8f90 100644 --- a/CreamInstaller/Forms/SelectDialogForm.Designer.cs +++ b/CreamInstaller/Forms/SelectDialogForm.Designer.cs @@ -33,7 +33,6 @@ 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(); @@ -53,21 +52,13 @@ 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.Location = new System.Drawing.Point(12, 14); filterTextBox.Name = "filterTextBox"; - filterTextBox.Size = new System.Drawing.Size(436, 23); + filterTextBox.PlaceholderText = "Enter the name of a game to search"; + filterTextBox.Size = new System.Drawing.Size(524, 23); filterTextBox.TabIndex = 0; filterTextBox.TextChanged += OnFilterTextChanged; // @@ -209,7 +200,6 @@ namespace CreamInstaller.Forms Controls.Add(acceptButton); Controls.Add(groupBox); Controls.Add(filterTextBox); - Controls.Add(filterLabel); FormBorderStyle = FormBorderStyle.FixedSingle; MaximizeBox = false; MinimizeBox = false; @@ -237,7 +227,6 @@ namespace CreamInstaller.Forms private Button saveButton; private CheckBox sortCheckBox; private Button uninstallAllButton; - private System.Windows.Forms.Label filterLabel; private System.Windows.Forms.TextBox filterTextBox; } } \ No newline at end of file diff --git a/CreamInstaller/Utility/ThemeManager.cs b/CreamInstaller/Utility/ThemeManager.cs index c588cce..eef74be 100644 --- a/CreamInstaller/Utility/ThemeManager.cs +++ b/CreamInstaller/Utility/ThemeManager.cs @@ -211,6 +211,7 @@ internal static class ThemeManager tb.BackColor = DarkBackAlt; tb.ForeColor = DarkFore; tb.BorderStyle = BorderStyle.FixedSingle; + NativeMethods.RefreshCueBanner(tb); break; // Layout panels set a consistent background @@ -269,6 +270,7 @@ internal static class ThemeManager tb.BackColor = LightBackAlt; tb.ForeColor = LightFore; tb.BorderStyle = BorderStyle.Fixed3D; + NativeMethods.RefreshCueBanner(tb); break; case TableLayoutPanel tlp: tlp.BackColor = LightBack; @@ -462,15 +464,54 @@ internal static class ThemeManager } } +/// +/// Wraps Win32 API calls that have no managed equivalent in WinForms. +/// These P/Invoke declarations are required because .NET does not expose +/// the underlying Windows messages or DWM attributes through its own APIs. +/// internal static class NativeMethods { + // DWM attribute index for enabling/disabling the immersive dark title bar. + // Documented in dwmapi.h; value 20 corresponds to DWMWA_USE_IMMERSIVE_DARK_MODE + // (Windows 10 build 19041+ / Windows 11). private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20; + // DwmSetWindowAttribute allows setting per-window Desktop Window Manager attributes. + // We use it here to flip the title bar to dark or light depending on the active theme, + // since WinForms has no built-in API to control title bar coloring. [System.Runtime.InteropServices.DllImport("dwmapi.dll")] private static extern int DwmSetWindowAttribute(System.IntPtr hwnd, int attr, ref int attrValue, int attrSize); + /// + /// Toggles the dark/light title bar chrome for the given window handle. + /// Pass 1 for dark mode, 0 for light mode. + /// internal static void EnableDarkTitleBar(System.IntPtr handle, int useDark) { _ = DwmSetWindowAttribute(handle, DWMWA_USE_IMMERSIVE_DARK_MODE, ref useDark, sizeof(int)); } + + // Win32 Edit control message that sets or updates the cue (placeholder) banner text. + // WinForms sets PlaceholderText once at creation time via this same message internally, + // but does not re-send it when the control's colors change. When we restyle a TextBox + // for dark/light mode the cue banner can disappear, so we must re-send the message + // manually to make the placeholder visible again. + private const int EM_SETCUEBANNER = 0x1501; + + // SendMessage is the standard Win32 mechanism for posting messages directly to a + // window/control handle. We use the Unicode variant so the placeholder string is + // transmitted without any ANSI conversion. + [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + private static extern System.IntPtr SendMessage(System.IntPtr hWnd, int msg, System.IntPtr wParam, string lParam); + + /// + /// Re-sends EM_SETCUEBANNER to the given TextBox so its placeholder text + /// is redrawn after a theme change has altered the control's background or foreground colors. + /// Does nothing if the control handle has not yet been created or the placeholder is empty. + /// + internal static void RefreshCueBanner(System.Windows.Forms.TextBox textBox) + { + if (textBox?.IsHandleCreated == true && textBox.PlaceholderText is { Length: > 0 }) + SendMessage(textBox.Handle, EM_SETCUEBANNER, (System.IntPtr)1, textBox.PlaceholderText); + } } \ No newline at end of file