Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions src/Core/src/Platform/Windows/MauiPasswordTextBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,27 +56,11 @@ static void OnPasswordPropertyChanged(DependencyObject dependencyObject, Depende
bool _internalChangeFlag;
int _cachedCursorPosition;
int _cachedTextLength;
readonly long _token;

public MauiPasswordTextBox()
{
TextChanging += OnNativeTextChanging;
TextChanged += OnNativeTextChanged;
_token = RegisterPropertyChangedCallback(TextBox.InputScopeProperty, OnInputScopePropertyChanged);
Unloaded += (s, e) =>
{
UnregisterPropertyChangedCallback(TextBox.InputScopeProperty, _token);
};
}

static void OnInputScopePropertyChanged(DependencyObject sender, DependencyProperty dp)
{
if (sender is not MauiPasswordTextBox mauiTxtBox || mauiTxtBox.IsPassword)
{
return;
}

mauiTxtBox.IsPassword = mauiTxtBox.InputScope?.Names?.Any(x => x.NameValue == InputScopeNameValue.Password) ?? false;
}

public bool IsPassword
Expand Down
1 change: 0 additions & 1 deletion src/Core/src/Platform/iOS/KeyboardExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ public static void ApplyKeyboard(this IUITextInputTraits textInput, Keyboard key
else if (keyboard == Keyboard.Password)
{
textInput.SetKeyboardType(UIKeyboardType.Default);
textInput.SetSecureTextEntry(true);
}
else if (keyboard is CustomKeyboard)
{
Expand Down
64 changes: 64 additions & 0 deletions src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,70 @@ await ValidatePropertyUpdatesValue(
unsetValue);
}


#if !ANDROID // Android fix is covered by PR https://github.com/dotnet/maui/pull/36280
[Fact(DisplayName = "Password Keyboard Respects IsPassword False Initially")]
public async Task PasswordKeyboardRespectsIsPasswordFalseInitially()
{
var entry = new EntryStub()
{
Keyboard = Keyboard.Password,
IsPassword = false,
Text = "1234"
};

await CreateHandlerAsync(entry);

// Initial state: Keyboard=Password but IsPassword=false → text should NOT be masked
await InvokeOnMainThreadAsync(() =>
{
Assert.False(GetNativeIsPassword(entry.Handler as EntryHandler),
"Entry with Keyboard=Password and IsPassword=false should not be masked on initial load");
});
}

[Fact(DisplayName = "Password Keyboard IsPassword Toggle Works Correctly")]
public async Task PasswordKeyboardIsPasswordToggleWorksCorrectly()
{
var entry = new EntryStub()
{
Keyboard = Keyboard.Password,
IsPassword = false,
Text = "1234"
};

await CreateHandlerAsync(entry);

// Initial: IsPassword=false → not masked
await InvokeOnMainThreadAsync(() =>
{
Assert.False(GetNativeIsPassword(entry.Handler as EntryHandler),
"Initial state: should not be masked");
});

// Toggle to IsPassword=true → masked
// EntryStub is not a BindableObject, so we must explicitly call UpdateValue after changing the property.
await InvokeOnMainThreadAsync(() =>
{
entry.IsPassword = true;
(entry.Handler as EntryHandler).UpdateValue(nameof(IEntry.IsPassword));
});
await InvokeOnMainThreadAsync(() =>
Assert.True(GetNativeIsPassword(entry.Handler as EntryHandler),
"After IsPassword=true: should be masked"));

// Toggle back to IsPassword=false → not masked
await InvokeOnMainThreadAsync(() =>
{
entry.IsPassword = false;
(entry.Handler as EntryHandler).UpdateValue(nameof(IEntry.IsPassword));
});
await InvokeOnMainThreadAsync(() =>
Assert.False(GetNativeIsPassword(entry.Handler as EntryHandler),
"After IsPassword=false: should not be masked"));
}
#endif

[Theory(DisplayName = "TextColor Updates Correctly")]
[InlineData(0xFFFF0000, 0xFF0000FF)]
[InlineData(0xFF0000FF, 0xFFFF0000)]
Expand Down
Loading