Browse Source

Merge pull request #1544 from AvaloniaUI/feature/password-box

PasswordChar support for TextBox
pull/1329/merge
Steven Kirk 8 years ago
committed by GitHub
parent
commit
ffbc42d6eb
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  2. 29
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  3. 17
      src/Avalonia.Controls/Primitives/AccessText.cs
  4. 7
      src/Avalonia.Controls/TextBlock.cs
  5. 35
      src/Avalonia.Controls/TextBox.cs
  6. 7
      src/Avalonia.Themes.Default/TextBox.xaml

10
samples/ControlCatalog/Pages/TextBoxPage.xaml

@ -10,10 +10,16 @@
<StackPanel Orientation="Vertical" Gap="8"> <StackPanel Orientation="Vertical" Gap="8">
<TextBox Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit." Width="200" /> <TextBox Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit." Width="200" />
<TextBox Width="200" Watermark="Watermark" /> <TextBox Width="200" Watermark="Watermark" />
<TextBox Width="200" <TextBox Width="200"
Watermark="Floating Watermark" Watermark="Floating Watermark"
UseFloatingWatermark="True" UseFloatingWatermark="True"
Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit."/> Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit."/>
<TextBox Width="200"
Watermark="Password Box"
UseFloatingWatermark="True"
PasswordChar="*"
Text="Password" />
<TextBox Width="200" Text="Left aligned text" TextAlignment="Left" /> <TextBox Width="200" Text="Left aligned text" TextAlignment="Left" />
<TextBox Width="200" Text="Center aligned text" TextAlignment="Center" /> <TextBox Width="200" Text="Center aligned text" TextAlignment="Center" />
<TextBox Width="200" Text="Right aligned text" TextAlignment="Right" /> <TextBox Width="200" Text="Right aligned text" TextAlignment="Right" />

29
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -17,6 +17,9 @@ namespace Avalonia.Controls.Presenters
o => o.CaretIndex, o => o.CaretIndex,
(o, v) => o.CaretIndex = v); (o, v) => o.CaretIndex = v);
public static readonly StyledProperty<char> PasswordCharProperty =
AvaloniaProperty.Register<TextPresenter, char>(nameof(PasswordChar));
public static readonly DirectProperty<TextPresenter, int> SelectionStartProperty = public static readonly DirectProperty<TextPresenter, int> SelectionStartProperty =
TextBox.SelectionStartProperty.AddOwner<TextPresenter>( TextBox.SelectionStartProperty.AddOwner<TextPresenter>(
o => o.SelectionStart, o => o.SelectionStart,
@ -63,6 +66,12 @@ namespace Avalonia.Controls.Presenters
} }
} }
public char PasswordChar
{
get => GetValue(PasswordCharProperty);
set => SetValue(PasswordCharProperty, value);
}
public int SelectionStart public int SelectionStart
{ {
get get
@ -202,9 +211,25 @@ namespace Avalonia.Controls.Presenters
} }
} }
protected override FormattedText CreateFormattedText(Size constraint) /// <summary>
/// Creates the <see cref="FormattedText"/> used to render the text.
/// </summary>
/// <param name="constraint">The constraint of the text.</param>
/// <param name="text">The text to generated the <see cref="FormattedText"/> for.</param>
/// <returns>A <see cref="FormattedText"/> object.</returns>
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 selectionStart = SelectionStart;
var selectionEnd = SelectionEnd; var selectionEnd = SelectionEnd;
var start = Math.Min(selectionStart, selectionEnd); var start = Math.Min(selectionStart, selectionEnd);

17
src/Avalonia.Controls/Primitives/AccessText.cs

@ -78,21 +78,10 @@ namespace Avalonia.Controls.Primitives
} }
} }
/// <summary> /// <inheritdoc/>
/// Creates the <see cref="FormattedText"/> used to render the text. protected override FormattedText CreateFormattedText(Size constraint, string text)
/// </summary>
/// <param name="constraint">The constraint of the text.</param>
/// <returns>A <see cref="FormattedText"/> object.</returns>
protected override FormattedText CreateFormattedText(Size constraint)
{ {
return new FormattedText return base.CreateFormattedText(constraint, StripAccessKey(text));
{
Constraint = constraint,
Typeface = new Typeface(FontFamily, FontSize, FontStyle, FontWeight),
Text = StripAccessKey(Text),
TextAlignment = TextAlignment,
Wrapping = TextWrapping,
};
} }
/// <summary> /// <summary>

7
src/Avalonia.Controls/TextBlock.cs

@ -197,7 +197,7 @@ namespace Avalonia.Controls
{ {
if (_formattedText == null) if (_formattedText == null)
{ {
_formattedText = CreateFormattedText(_constraint); _formattedText = CreateFormattedText(_constraint, Text);
} }
return _formattedText; return _formattedText;
@ -348,14 +348,15 @@ namespace Avalonia.Controls
/// Creates the <see cref="FormattedText"/> used to render the text. /// Creates the <see cref="FormattedText"/> used to render the text.
/// </summary> /// </summary>
/// <param name="constraint">The constraint of the text.</param> /// <param name="constraint">The constraint of the text.</param>
/// <param name="text">The text to format.</param>
/// <returns>A <see cref="FormattedText"/> object.</returns> /// <returns>A <see cref="FormattedText"/> object.</returns>
protected virtual FormattedText CreateFormattedText(Size constraint) protected virtual FormattedText CreateFormattedText(Size constraint, string text)
{ {
return new FormattedText return new FormattedText
{ {
Constraint = constraint, Constraint = constraint,
Typeface = new Typeface(FontFamily, FontSize, FontStyle, FontWeight), Typeface = new Typeface(FontFamily, FontSize, FontStyle, FontWeight),
Text = Text ?? string.Empty, Text = text ?? string.Empty,
TextAlignment = TextAlignment, TextAlignment = TextAlignment,
Wrapping = TextWrapping, Wrapping = TextWrapping,
}; };

35
src/Avalonia.Controls/TextBox.cs

@ -30,10 +30,13 @@ namespace Avalonia.Controls
nameof(CaretIndex), nameof(CaretIndex),
o => o.CaretIndex, o => o.CaretIndex,
(o, v) => o.CaretIndex = v); (o, v) => o.CaretIndex = v);
public static readonly StyledProperty<bool> IsReadOnlyProperty = public static readonly StyledProperty<bool> IsReadOnlyProperty =
AvaloniaProperty.Register<TextBox, bool>(nameof(IsReadOnly)); AvaloniaProperty.Register<TextBox, bool>(nameof(IsReadOnly));
public static readonly StyledProperty<char> PasswordCharProperty =
AvaloniaProperty.Register<TextBox, char>(nameof(PasswordChar));
public static readonly DirectProperty<TextBox, int> SelectionStartProperty = public static readonly DirectProperty<TextBox, int> SelectionStartProperty =
AvaloniaProperty.RegisterDirect<TextBox, int>( AvaloniaProperty.RegisterDirect<TextBox, int>(
nameof(SelectionStart), nameof(SelectionStart),
@ -87,7 +90,7 @@ namespace Avalonia.Controls
private UndoRedoHelper<UndoRedoState> _undoRedoHelper; private UndoRedoHelper<UndoRedoState> _undoRedoHelper;
private bool _isUndoingRedoing; private bool _isUndoingRedoing;
private bool _ignoreTextChanges; private bool _ignoreTextChanges;
private static readonly string[] invalidCharacters = new String[1]{"\u007f"}; private static readonly string[] invalidCharacters = new String[1] { "\u007f" };
static TextBox() static TextBox()
{ {
@ -116,7 +119,7 @@ namespace Avalonia.Controls
ScrollViewer.HorizontalScrollBarVisibilityProperty, ScrollViewer.HorizontalScrollBarVisibilityProperty,
horizontalScrollBarVisibility, horizontalScrollBarVisibility,
BindingPriority.Style); BindingPriority.Style);
_undoRedoHelper = new UndoRedoHelper<UndoRedoState>(this); _undoRedoHelper = new UndoRedoHelper<UndoRedoState>(this);
} }
public bool AcceptsReturn public bool AcceptsReturn
@ -147,13 +150,19 @@ namespace Avalonia.Controls
_undoRedoHelper.UpdateLastState(); _undoRedoHelper.UpdateLastState();
} }
} }
public bool IsReadOnly public bool IsReadOnly
{ {
get { return GetValue(IsReadOnlyProperty); } get { return GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); } set { SetValue(IsReadOnlyProperty, value); }
} }
public char PasswordChar
{
get => GetValue(PasswordCharProperty);
set => SetValue(PasswordCharProperty, value);
}
public int SelectionStart public int SelectionStart
{ {
get get
@ -237,7 +246,7 @@ namespace Avalonia.Controls
_presenter = e.NameScope.Get<TextPresenter>("PART_TextPresenter"); _presenter = e.NameScope.Get<TextPresenter>("PART_TextPresenter");
_presenter.Cursor = new Cursor(StandardCursorType.Ibeam); _presenter.Cursor = new Cursor(StandardCursorType.Ibeam);
if(IsFocused) if (IsFocused)
{ {
_presenter.ShowCaret(); _presenter.ShowCaret();
} }
@ -349,7 +358,10 @@ namespace Avalonia.Controls
case Key.C: case Key.C:
if (modifiers == InputModifiers.Control) if (modifiers == InputModifiers.Control)
{ {
Copy(); if (!IsPasswordBox)
{
Copy();
}
handled = true; handled = true;
} }
break; break;
@ -357,8 +369,11 @@ namespace Avalonia.Controls
case Key.X: case Key.X:
if (modifiers == InputModifiers.Control) if (modifiers == InputModifiers.Control)
{ {
Copy(); if (!IsPasswordBox)
DeleteSelection(); {
Copy();
DeleteSelection();
}
handled = true; handled = true;
} }
break; break;
@ -578,7 +593,7 @@ namespace Avalonia.Controls
DataValidationErrors.SetError(this, status.Error); DataValidationErrors.SetError(this, status.Error);
} }
} }
private int CoerceCaretIndex(int value) => CoerceCaretIndex(value, Text?.Length ?? 0); private int CoerceCaretIndex(int value) => CoerceCaretIndex(value, Text?.Length ?? 0);
private int CoerceCaretIndex(int value, int length) private int CoerceCaretIndex(int value, int length)
@ -856,6 +871,8 @@ namespace Avalonia.Controls
SelectionEnd = CaretIndex; SelectionEnd = CaretIndex;
} }
private bool IsPasswordBox => PasswordChar != default(char);
UndoRedoState UndoRedoHelper<UndoRedoState>.IUndoRedoHost.UndoRedoState UndoRedoState UndoRedoHelper<UndoRedoState>.IUndoRedoHost.UndoRedoState
{ {
get { return new UndoRedoState(Text, CaretIndex); } get { return new UndoRedoState(Text, CaretIndex); }

7
src/Avalonia.Themes.Default/TextBox.xaml

@ -11,7 +11,7 @@
BorderBrush="{TemplateBinding BorderBrush}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"> BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel Margin="{TemplateBinding Padding}"> <DockPanel Margin="{TemplateBinding Padding}">
<TextBlock Name="floatingWatermark" <TextBlock Name="floatingWatermark"
Foreground="{DynamicResource ThemeAccentBrush}" Foreground="{DynamicResource ThemeAccentBrush}"
FontSize="{DynamicResource FontSizeSmall}" FontSize="{DynamicResource FontSizeSmall}"
@ -31,7 +31,7 @@
<DataValidationErrors> <DataValidationErrors>
<ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}" <ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"> VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
<Panel> <Panel>
<TextBlock Name="watermark" <TextBlock Name="watermark"
Opacity="0.5" Opacity="0.5"
@ -43,7 +43,8 @@
SelectionStart="{TemplateBinding SelectionStart}" SelectionStart="{TemplateBinding SelectionStart}"
SelectionEnd="{TemplateBinding SelectionEnd}" SelectionEnd="{TemplateBinding SelectionEnd}"
TextAlignment="{TemplateBinding TextAlignment}" TextAlignment="{TemplateBinding TextAlignment}"
TextWrapping="{TemplateBinding TextWrapping}"/> TextWrapping="{TemplateBinding TextWrapping}"
PasswordChar="{TemplateBinding PasswordChar}"/>
</Panel> </Panel>
</ScrollViewer> </ScrollViewer>
</DataValidationErrors> </DataValidationErrors>

Loading…
Cancel
Save