diff --git a/samples/ControlCatalog/Pages/TextBoxPage.xaml b/samples/ControlCatalog/Pages/TextBoxPage.xaml index 7bfcad6d51..db1c9cb69d 100644 --- a/samples/ControlCatalog/Pages/TextBoxPage.xaml +++ b/samples/ControlCatalog/Pages/TextBoxPage.xaml @@ -10,10 +10,16 @@ - + + diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index d2d4151e3d..c241466aea 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -17,6 +17,9 @@ namespace Avalonia.Controls.Presenters o => o.CaretIndex, (o, v) => o.CaretIndex = v); + public static readonly StyledProperty PasswordCharProperty = + AvaloniaProperty.Register(nameof(PasswordChar)); + public static readonly DirectProperty SelectionStartProperty = TextBox.SelectionStartProperty.AddOwner( o => o.SelectionStart, @@ -63,6 +66,12 @@ namespace Avalonia.Controls.Presenters } } + public char PasswordChar + { + get => GetValue(PasswordCharProperty); + set => SetValue(PasswordCharProperty, value); + } + public int SelectionStart { get @@ -202,9 +211,25 @@ namespace Avalonia.Controls.Presenters } } - protected override FormattedText CreateFormattedText(Size constraint) + /// + /// Creates the used to render the text. + /// + /// The constraint of the text. + /// The text to generated the for. + /// A object. + protected override FormattedText CreateFormattedText(Size constraint, string text) { - var result = base.CreateFormattedText(constraint); + FormattedText result = null; + + if (PasswordChar != default(char)) + { + result = base.CreateFormattedText(constraint, new string(PasswordChar, Text.Length)); + } + else + { + result = base.CreateFormattedText(constraint, Text); + } + var selectionStart = SelectionStart; var selectionEnd = SelectionEnd; var start = Math.Min(selectionStart, selectionEnd); diff --git a/src/Avalonia.Controls/Primitives/AccessText.cs b/src/Avalonia.Controls/Primitives/AccessText.cs index 4bb80e6d3f..32a0efc440 100644 --- a/src/Avalonia.Controls/Primitives/AccessText.cs +++ b/src/Avalonia.Controls/Primitives/AccessText.cs @@ -78,21 +78,10 @@ namespace Avalonia.Controls.Primitives } } - /// - /// Creates the used to render the text. - /// - /// The constraint of the text. - /// A object. - protected override FormattedText CreateFormattedText(Size constraint) + /// + protected override FormattedText CreateFormattedText(Size constraint, string text) { - return new FormattedText - { - Constraint = constraint, - Typeface = new Typeface(FontFamily, FontSize, FontStyle, FontWeight), - Text = StripAccessKey(Text), - TextAlignment = TextAlignment, - Wrapping = TextWrapping, - }; + return base.CreateFormattedText(constraint, StripAccessKey(text)); } /// diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 88a9fe077d..1fee309360 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -197,7 +197,7 @@ namespace Avalonia.Controls { if (_formattedText == null) { - _formattedText = CreateFormattedText(_constraint); + _formattedText = CreateFormattedText(_constraint, Text); } return _formattedText; @@ -348,14 +348,15 @@ namespace Avalonia.Controls /// Creates the used to render the text. /// /// The constraint of the text. + /// The text to format. /// A object. - protected virtual FormattedText CreateFormattedText(Size constraint) + protected virtual FormattedText CreateFormattedText(Size constraint, string text) { return new FormattedText { Constraint = constraint, Typeface = new Typeface(FontFamily, FontSize, FontStyle, FontWeight), - Text = Text ?? string.Empty, + Text = text ?? string.Empty, TextAlignment = TextAlignment, Wrapping = TextWrapping, }; diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 890926db54..874d2a3229 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -30,10 +30,13 @@ namespace Avalonia.Controls nameof(CaretIndex), o => o.CaretIndex, (o, v) => o.CaretIndex = v); - + public static readonly StyledProperty IsReadOnlyProperty = AvaloniaProperty.Register(nameof(IsReadOnly)); + public static readonly StyledProperty PasswordCharProperty = + AvaloniaProperty.Register(nameof(PasswordChar)); + public static readonly DirectProperty SelectionStartProperty = AvaloniaProperty.RegisterDirect( nameof(SelectionStart), @@ -87,7 +90,7 @@ namespace Avalonia.Controls private UndoRedoHelper _undoRedoHelper; private bool _isUndoingRedoing; private bool _ignoreTextChanges; - private static readonly string[] invalidCharacters = new String[1]{"\u007f"}; + private static readonly string[] invalidCharacters = new String[1] { "\u007f" }; static TextBox() { @@ -116,7 +119,7 @@ namespace Avalonia.Controls ScrollViewer.HorizontalScrollBarVisibilityProperty, horizontalScrollBarVisibility, BindingPriority.Style); - _undoRedoHelper = new UndoRedoHelper(this); + _undoRedoHelper = new UndoRedoHelper(this); } public bool AcceptsReturn @@ -147,13 +150,19 @@ namespace Avalonia.Controls _undoRedoHelper.UpdateLastState(); } } - + public bool IsReadOnly { get { return GetValue(IsReadOnlyProperty); } set { SetValue(IsReadOnlyProperty, value); } } + public char PasswordChar + { + get => GetValue(PasswordCharProperty); + set => SetValue(PasswordCharProperty, value); + } + public int SelectionStart { get @@ -237,7 +246,7 @@ namespace Avalonia.Controls _presenter = e.NameScope.Get("PART_TextPresenter"); _presenter.Cursor = new Cursor(StandardCursorType.Ibeam); - if(IsFocused) + if (IsFocused) { _presenter.ShowCaret(); } @@ -349,7 +358,10 @@ namespace Avalonia.Controls case Key.C: if (modifiers == InputModifiers.Control) { - Copy(); + if (!IsPasswordBox) + { + Copy(); + } handled = true; } break; @@ -357,8 +369,11 @@ namespace Avalonia.Controls case Key.X: if (modifiers == InputModifiers.Control) { - Copy(); - DeleteSelection(); + if (!IsPasswordBox) + { + Copy(); + DeleteSelection(); + } handled = true; } break; @@ -578,7 +593,7 @@ namespace Avalonia.Controls DataValidationErrors.SetError(this, status.Error); } } - + private int CoerceCaretIndex(int value) => CoerceCaretIndex(value, Text?.Length ?? 0); private int CoerceCaretIndex(int value, int length) @@ -856,6 +871,8 @@ namespace Avalonia.Controls SelectionEnd = CaretIndex; } + private bool IsPasswordBox => PasswordChar != default(char); + UndoRedoState UndoRedoHelper.IUndoRedoHost.UndoRedoState { get { return new UndoRedoState(Text, CaretIndex); } diff --git a/src/Avalonia.Themes.Default/TextBox.xaml b/src/Avalonia.Themes.Default/TextBox.xaml index e228aebf58..c57e5b792d 100644 --- a/src/Avalonia.Themes.Default/TextBox.xaml +++ b/src/Avalonia.Themes.Default/TextBox.xaml @@ -11,7 +11,7 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> - + - + + TextWrapping="{TemplateBinding TextWrapping}" + PasswordChar="{TemplateBinding PasswordChar}"/>