From 4e0632cf4280adf6d5ec0d5bf2e86b4488a3ec0f Mon Sep 17 00:00:00 2001 From: yoyo Date: Mon, 6 Jul 2026 19:32:37 +0800 Subject: [PATCH] Allow copying and cutting revealed TextBox passwords (#21710) --- src/Avalonia.Controls/TextBox.cs | 10 +++--- .../TextBoxTests.cs | 34 +++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index eb72638744..0f2e3d51ce 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1041,7 +1041,9 @@ namespace Avalonia.Controls UpdatePseudoclasses(); UpdateCommandStates(); } - else if (change.Property == IsReadOnlyProperty || change.Property == PasswordCharProperty) + else if (change.Property == IsReadOnlyProperty || + change.Property == PasswordCharProperty || + change.Property == RevealPasswordProperty) { UpdateCommandStates(); } @@ -1746,7 +1748,7 @@ namespace Avalonia.Controls else { // We select the current held word, or the whole hidden content - if (IsPasswordBox && !RevealPassword) + if (IsPasswordBox) { _wordSelectionStart = -1; @@ -1881,7 +1883,7 @@ namespace Avalonia.Controls private void SelectWord(string text, int caretIndex, int selectionStart, int selectionEnd) { - if (IsPasswordBox && !RevealPassword) + if (IsPasswordBox) { // double-clicking in a cloaked single-line password box selects all text // see https://github.com/AvaloniaUI/Avalonia/issues/14956 @@ -2530,7 +2532,7 @@ namespace Avalonia.Controls PseudoClasses.Set(":touch-mode", _isInTouchMode); } - private bool IsPasswordBox => PasswordChar != default(char); + private bool IsPasswordBox => PasswordChar != default(char) && !RevealPassword; UndoRedoState UndoRedoHelper.IUndoRedoHost.UndoRedoState { diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index bf0af90b27..46af86b98d 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -1527,6 +1527,40 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Command_States_Update_When_RevealPassword_Changes() + { + using (UnitTestApplication.Start(Services)) + { + var tb = new TextBox + { + Template = CreateTemplate(), + Text = "1234", + PasswordChar = '*', + SelectionStart = 1, + SelectionEnd = 3, + }; + + tb.Measure(Size.Infinity); + + Assert.False(tb.CanCopy); + Assert.False(tb.CanCut); + Assert.True(tb.CanPaste); + + tb.RevealPassword = true; + + Assert.True(tb.CanCopy); + Assert.True(tb.CanCut); + Assert.True(tb.CanPaste); + + tb.RevealPassword = false; + + Assert.False(tb.CanCopy); + Assert.False(tb.CanCut); + Assert.True(tb.CanPaste); + } + } + [Fact] public void ReadOnly_Editing_Hotkeys_Do_Not_Modify_Text_Or_Undo_State() {