From 35c3d92df477a03108f6279674da0e4758e45bd7 Mon Sep 17 00:00:00 2001 From: Zero <1270128439@qq.com> Date: Fri, 7 Aug 2026 13:12:17 +0800 Subject: [PATCH] Let the edit-server dialog reveal the password MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The password field has always been a PasswordBox, and that was the problem: PasswordBox's built-in "peek" button only appears once the box has taken focus *and* the user has typed a character. Editing an existing server assigns Password in code, so peek never showed up and the stored value could not be read back at all. Force PasswordRevealMode to Hidden and put an explicit reveal toggle in the field header, to the right of the label. The glyph swaps between RedEye and Hide; tooltip and automation name follow. No extra form row — the dialog already has 30-odd fields. Scoped to the password field only, per the issue. The other credential fields in this form (UUID, Reality keys, WireGuard keys) are plain TextBoxes and already show their values. Closes #124 Co-Authored-By: Claude Opus 5 --- Helpers/L.cs | 2 ++ Services/DialogService.cs | 47 +++++++++++++++++++++++++++++++++++- Strings/en-US/Resources.resw | 6 +++++ Strings/zh-CN/Resources.resw | 6 +++++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/Helpers/L.cs b/Helpers/L.cs index 7b3161a..94b6ef5 100644 --- a/Helpers/L.cs +++ b/Helpers/L.cs @@ -42,6 +42,8 @@ public static class L public static string EditServer_Protocol => Loc.GetString("EditServer_Protocol"); public static string EditServer_Encryption => Loc.GetString("EditServer_Encryption"); public static string EditServer_Password => Loc.GetString("EditServer_Password"); + public static string EditServer_ShowPassword => Loc.GetString("EditServer_ShowPassword"); + public static string EditServer_HidePassword => Loc.GetString("EditServer_HidePassword"); public static string EditServer_Transport => Loc.GetString("EditServer_Transport"); public static string EditServer_Path => Loc.GetString("EditServer_Path"); public static string EditServer_WsHost => Loc.GetString("EditServer_WsHost"); diff --git a/Services/DialogService.cs b/Services/DialogService.cs index 256f6cb..a62b060 100644 --- a/Services/DialogService.cs +++ b/Services/DialogService.cs @@ -136,7 +136,7 @@ void SyncDialogButtons() cmbEncryption.Items.Add(existingEnc); cmbEncryption.SelectedItem = existing?.Encryption ?? "aes-128-gcm"; var txtUsername = new TextBox { Header = L.EditServer_SocksUsername, Text = existing?.Username ?? string.Empty }; - var txtPassword = new PasswordBox { Header = L.EditServer_Password, Password = existing?.Password ?? string.Empty }; + var txtPassword = CreateRevealablePasswordBox(L.EditServer_Password, existing?.Password); var txtUuid = new TextBox { Header = "UUID (VMess / VLESS)", Text = existing?.Uuid ?? string.Empty }; var numAlterId = new NumberBox { Header = "AlterId (VMess)", Value = existing?.AlterId ?? 0, Minimum = 0, Maximum = 65535 }; @@ -1288,6 +1288,51 @@ public async Task ShowDnsSettingsDialogAsync(AppSettings settings, bool is private static Border Wrap(FrameworkElement child) => new Border { Child = child }; + + private const string RevealGlyph = "\uE7B3"; // RedEye + private const string ConcealGlyph = "\uED1A"; // Hide + + /// + /// A PasswordBox whose header carries a reveal toggle on the right. + /// + /// PasswordRevealMode is forced to Hidden rather than left at the default Peek: the + /// built-in peek button only shows up once the box has focus and the user has typed + /// a character. An existing server's password is assigned here in code, so peek is + /// dead on arrival and the value can never be read back (issue #124). + /// + /// + private static PasswordBox CreateRevealablePasswordBox(string header, string? value) + { + var box = new PasswordBox { Password = value ?? string.Empty }; + + var icon = new FontIcon { FontSize = 14 }; + var toggle = new Button + { + Content = icon, + Padding = new Thickness(6, 0, 6, 0), + Height = 22, + VerticalAlignment = VerticalAlignment.Center, + }; + if (Application.Current.Resources.TryGetValue("SubtleButtonStyle", out var subtleStyle)) + toggle.Style = (Style)subtleStyle; + + void SetRevealed(bool revealed) + { + box.PasswordRevealMode = revealed ? PasswordRevealMode.Visible : PasswordRevealMode.Hidden; + icon.Glyph = revealed ? ConcealGlyph : RevealGlyph; + + var label = revealed ? L.EditServer_HidePassword : L.EditServer_ShowPassword; + ToolTipService.SetToolTip(toggle, label); + AutomationProperties.SetName(toggle, label); + } + + SetRevealed(false); + toggle.Click += (_, _) => SetRevealed(box.PasswordRevealMode != PasswordRevealMode.Visible); + + box.Header = CreateLabelRow(header, toggle); + return box; + } + /// Multi-line raw-JSON editor box (Finalmask / XHTTP extra). private static TextBox CreateJsonTextBox(string header, string? value) => new() { diff --git a/Strings/en-US/Resources.resw b/Strings/en-US/Resources.resw index 706c359..5181e04 100644 --- a/Strings/en-US/Resources.resw +++ b/Strings/en-US/Resources.resw @@ -428,6 +428,12 @@ Password + + Show password + + + Hide password + Transport Protocol diff --git a/Strings/zh-CN/Resources.resw b/Strings/zh-CN/Resources.resw index 9d0b27d..dc0d77f 100644 --- a/Strings/zh-CN/Resources.resw +++ b/Strings/zh-CN/Resources.resw @@ -428,6 +428,12 @@ 密码 + + 显示密码 + + + 隐藏密码 + 传输协议