Преглед изворни кода

Merge pull request #4422 from AvaloniaUI/add-content-presenters-fluent-textbox

Add content presenters fluent textbox
danwalmsley пре 5 година
родитељ
комит
e8d6462a0a

+ 3 - 0
samples/ControlCatalog/Pages/TextBoxPage.xaml

@@ -20,6 +20,7 @@
 
         <TextBox Width="200"
                  Watermark="Password Box"
+                 Classes="revealPasswordButton"
                  UseFloatingWatermark="True"
                  PasswordChar="*"
                  Text="Password" />
@@ -37,6 +38,8 @@
                  Text="Multiline TextBox with TextWrapping.&#xD;&#xD;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." />
         <TextBox AcceptsReturn="True" Width="200" Height="125"
                  Text="Multiline TextBox with no TextWrapping.&#xD;&#xD;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." />
+        
+        <TextBox Classes="clearButton" Text="Clear Content" Width="200" FontWeight="Normal" FontStyle="Normal" Watermark="Watermark" FontFamily="avares://ControlCatalog/Assets/Fonts#Source Sans Pro"/>
       </StackPanel>
         <StackPanel Orientation="Vertical" Spacing="8">
             <TextBlock Classes="h2">resm fonts</TextBlock>

+ 12 - 3
src/Avalonia.Controls/Presenters/TextPresenter.cs

@@ -14,6 +14,9 @@ namespace Avalonia.Controls.Presenters
                 o => o.CaretIndex,
                 (o, v) => o.CaretIndex = v);
 
+        public static readonly StyledProperty<bool> RevealPasswordProperty =
+            AvaloniaProperty.Register<TextPresenter, bool>(nameof(RevealPassword));
+
         public static readonly StyledProperty<char> PasswordCharProperty =
             AvaloniaProperty.Register<TextPresenter, char>(nameof(PasswordChar));
 
@@ -75,7 +78,7 @@ namespace Avalonia.Controls.Presenters
         static TextPresenter()
         {
             AffectsRender<TextPresenter>(SelectionBrushProperty);
-            AffectsMeasure<TextPresenter>(TextProperty, PasswordCharProperty, 
+            AffectsMeasure<TextPresenter>(TextProperty, PasswordCharProperty, RevealPasswordProperty, 
                 TextAlignmentProperty, TextWrappingProperty, TextBlock.FontSizeProperty,
                 TextBlock.FontStyleProperty, TextBlock.FontWeightProperty, TextBlock.FontFamilyProperty);
 
@@ -84,7 +87,7 @@ namespace Avalonia.Controls.Presenters
                 TextBlock.FontSizeProperty.Changed, TextBlock.FontStyleProperty.Changed, 
                 TextBlock.FontWeightProperty.Changed, TextBlock.FontFamilyProperty.Changed,
                 SelectionStartProperty.Changed, SelectionEndProperty.Changed,
-                SelectionForegroundBrushProperty.Changed, PasswordCharProperty.Changed
+                SelectionForegroundBrushProperty.Changed, PasswordCharProperty.Changed, RevealPasswordProperty.Changed
             ).AddClassHandler<TextPresenter>((x, _) => x.InvalidateFormattedText());
 
             CaretIndexProperty.Changed.AddClassHandler<TextPresenter>((x, e) => x.CaretIndexChanged((int)e.NewValue));
@@ -210,6 +213,12 @@ namespace Avalonia.Controls.Presenters
             set => SetValue(PasswordCharProperty, value);
         }
 
+        public bool RevealPassword
+        {
+            get => GetValue(RevealPasswordProperty);
+            set => SetValue(RevealPasswordProperty, value);
+        }
+
         public IBrush SelectionBrush
         {
             get => GetValue(SelectionBrushProperty);
@@ -426,7 +435,7 @@ namespace Avalonia.Controls.Presenters
 
             var text = Text;
 
-            if (PasswordChar != default(char))
+            if (PasswordChar != default(char) && !RevealPassword)
             {
                 result = CreateFormattedTextInternal(_constraint, new string(PasswordChar, text?.Length ?? 0));
             }

+ 52 - 2
src/Avalonia.Controls/TextBox.cs

@@ -95,6 +95,15 @@ namespace Avalonia.Controls
             AvaloniaProperty.RegisterDirect<TextBox, string>(nameof(NewLine),
                 textbox => textbox.NewLine, (textbox, newline) => textbox.NewLine = newline);
 
+        public static readonly StyledProperty<object> InnerLeftContentProperty =
+            AvaloniaProperty.Register<TextBox, object>(nameof(InnerLeftContent));
+
+        public static readonly StyledProperty<object> InnerRightContentProperty =
+            AvaloniaProperty.Register<TextBox, object>(nameof(InnerRightContent));
+
+        public static readonly StyledProperty<bool> RevealPasswordProperty =
+            AvaloniaProperty.Register<TextBox, bool>(nameof(RevealPassword));
+
         struct UndoRedoState : IEquatable<UndoRedoState>
         {
             public string Text { get; }
@@ -148,6 +157,8 @@ namespace Avalonia.Controls
                 horizontalScrollBarVisibility,
                 BindingPriority.Style);
             _undoRedoHelper = new UndoRedoHelper<UndoRedoState>(this);
+
+            UpdatePseudoclasses();
         }
 
         public bool AcceptsReturn
@@ -285,7 +296,7 @@ namespace Avalonia.Controls
                 else
                 {
                     HandleTextInput(value);
-                } 
+                }
                 _undoRedoHelper.Snapshot();
             }
         }
@@ -326,6 +337,24 @@ namespace Avalonia.Controls
             set { SetValue(UseFloatingWatermarkProperty, value); }
         }
 
+        public object InnerLeftContent
+        {
+            get { return GetValue(InnerLeftContentProperty); }
+            set { SetValue(InnerLeftContentProperty, value); }
+        }
+
+        public object InnerRightContent
+        {
+            get { return GetValue(InnerRightContentProperty); }
+            set { SetValue(InnerRightContentProperty, value); }
+        }
+
+        public bool RevealPassword
+        {
+            get { return GetValue(RevealPasswordProperty); }
+            set { SetValue(RevealPasswordProperty, value); }
+        }
+
         public TextWrapping TextWrapping
         {
             get { return GetValue(TextWrappingProperty); }
@@ -351,6 +380,16 @@ namespace Avalonia.Controls
             }
         }
 
+        protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
+        {
+            base.OnPropertyChanged(change);
+
+            if (change.Property == TextProperty)
+            {
+                UpdatePseudoclasses();
+            }
+        }
+
         protected override void OnGotFocus(GotFocusEventArgs e)
         {
             base.OnGotFocus(e);
@@ -374,6 +413,7 @@ namespace Avalonia.Controls
             SelectionStart = 0;
             SelectionEnd = 0;
             _presenter?.HideCaret();
+            RevealPassword = false;
         }
 
         protected override void OnTextInput(TextInputEventArgs e)
@@ -756,7 +796,7 @@ namespace Avalonia.Controls
                     // if it did not, we change the selection to where the user clicked
                     var firstSelection = Math.Min(SelectionStart, SelectionEnd);
                     var lastSelection = Math.Max(SelectionStart, SelectionEnd);
-                    var didClickInSelection = SelectionStart != SelectionEnd && 
+                    var didClickInSelection = SelectionStart != SelectionEnd &&
                         caretIndex >= firstSelection && caretIndex <= lastSelection;
                     if (!didClickInSelection)
                     {
@@ -803,6 +843,11 @@ namespace Avalonia.Controls
             }
         }
 
+        public void Clear()
+        {
+            Text = string.Empty;
+        }
+
         private int DeleteCharacter(int index)
         {
             var start = index + 1;
@@ -1068,6 +1113,11 @@ namespace Avalonia.Controls
             SelectionEnd = CaretIndex;
         }
 
+        private void UpdatePseudoclasses()
+        {
+            PseudoClasses.Set(":empty", string.IsNullOrWhiteSpace(Text));
+        }
+
         private bool IsPasswordBox => PasswordChar != default(char);
 
         UndoRedoState UndoRedoHelper<UndoRedoState>.IUndoRedoHost.UndoRedoState

+ 10 - 5
src/Avalonia.Themes.Default/TextBox.xaml

@@ -35,14 +35,16 @@
             <DataValidationErrors>
               <ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
                             VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
-
-                <Panel>
+                
+                <Grid ColumnDefinitions="Auto,*,Auto">
+                  <ContentPresenter Grid.Column="0" Grid.ColumnSpan="1" Content="{TemplateBinding InnerLeftContent}"/>
                   <TextBlock Name="watermark"
                              Opacity="0.5"
                              Text="{TemplateBinding Watermark}"
                              TextAlignment="{TemplateBinding TextAlignment}"
                              TextWrapping="{TemplateBinding TextWrapping}"
-                             IsVisible="{TemplateBinding Text, Converter={x:Static StringConverters.IsNullOrEmpty}}"/>
+                             IsVisible="{TemplateBinding Text, Converter={x:Static StringConverters.IsNullOrEmpty}}"
+                             Grid.Column="1" Grid.ColumnSpan="1"/>
                   <TextPresenter Name="PART_TextPresenter"
                                  Text="{TemplateBinding Text, Mode=TwoWay}"
                                  CaretIndex="{TemplateBinding CaretIndex}"
@@ -51,10 +53,13 @@
                                  TextAlignment="{TemplateBinding TextAlignment}"
                                  TextWrapping="{TemplateBinding TextWrapping}"
                                  PasswordChar="{TemplateBinding PasswordChar}"
+                                 RevealPassword="{TemplateBinding RevealPassword}"
                                  SelectionBrush="{TemplateBinding SelectionBrush}"
                                  SelectionForegroundBrush="{TemplateBinding SelectionForegroundBrush}"
-                                 CaretBrush="{TemplateBinding CaretBrush}"/>
-                </Panel>
+                                 CaretBrush="{TemplateBinding CaretBrush}"
+                                 Grid.Column="1" Grid.ColumnSpan="1"/>
+                  <ContentPresenter Grid.Column="2" Grid.ColumnSpan="1" Content="{TemplateBinding InnerRightContent}"/>
+                </Grid>
               </ScrollViewer>
             </DataValidationErrors>
           </DockPanel>

+ 189 - 61
src/Avalonia.Themes.Fluent/TextBox.xaml

@@ -4,13 +4,18 @@
       <TextBox Text="Sample"
                FontSize="20"
                Width="100"
+               Classes="clearButton"
                TextAlignment="Center"/>
     </Border>
   </Design.PreviewWith>
   
   <Styles.Resources>
     <Thickness x:Key="TextBoxTopHeaderMargin">0,0,0,4</Thickness>
+    <StreamGeometry x:Key="TextBoxClearButtonData">M 11.416016,10 20,1.4160156 18.583984,0 10,8.5839846 1.4160156,0 0,1.4160156 8.5839844,10 0,18.583985 1.4160156,20 10,11.416015 18.583984,20 20,18.583985 Z</StreamGeometry>
+    <StreamGeometry x:Key="PasswordBoxRevealButtonData">m10.051 7.0032c2.215 0 4.0105 1.7901 4.0105 3.9984s-1.7956 3.9984-4.0105 3.9984c-2.215 0-4.0105-1.7901-4.0105-3.9984s1.7956-3.9984 4.0105-3.9984zm0 1.4994c-1.3844 0-2.5066 1.1188-2.5066 2.499s1.1222 2.499 2.5066 2.499 2.5066-1.1188 2.5066-2.499-1.1222-2.499-2.5066-2.499zm0-5.0026c4.6257 0 8.6188 3.1487 9.7267 7.5613 0.10085 0.40165-0.14399 0.80877-0.54686 0.90931-0.40288 0.10054-0.81122-0.14355-0.91208-0.54521-0.94136-3.7492-4.3361-6.4261-8.2678-6.4261-3.9334 0-7.3292 2.6792-8.2689 6.4306-0.10063 0.40171-0.50884 0.64603-0.91177 0.54571s-0.648-0.5073-0.54737-0.90901c1.106-4.4152 5.1003-7.5667 9.728-7.5667z</StreamGeometry>
+    <StreamGeometry x:Key="PasswordBoxHideButtonData">m0.21967 0.21965c-0.26627 0.26627-0.29047 0.68293-0.07262 0.97654l0.07262 0.08412 4.0346 4.0346c-1.922 1.3495-3.3585 3.365-3.9554 5.7495-0.10058 0.4018 0.14362 0.8091 0.54543 0.9097 0.40182 0.1005 0.80909-0.1436 0.90968-0.5455 0.52947-2.1151 1.8371-3.8891 3.5802-5.0341l1.8096 1.8098c-0.70751 0.7215-1.1438 1.71-1.1438 2.8003 0 2.2092 1.7909 4 4 4 1.0904 0 2.0788-0.4363 2.8004-1.1438l5.9193 5.9195c0.2929 0.2929 0.7677 0.2929 1.0606 0 0.2663-0.2662 0.2905-0.6829 0.0726-0.9765l-0.0726-0.0841-6.1135-6.1142 0.0012-0.0015-1.2001-1.1979-2.8699-2.8693 2e-3 -8e-4 -2.8812-2.8782 0.0012-0.0018-1.1333-1.1305-4.3064-4.3058c-0.29289-0.29289-0.76777-0.29289-1.0607 0zm7.9844 9.0458 3.5351 3.5351c-0.45 0.4358-1.0633 0.704-1.7392 0.704-1.3807 0-2.5-1.1193-2.5-2.5 0-0.6759 0.26824-1.2892 0.7041-1.7391zm1.7959-5.7655c-1.0003 0-1.9709 0.14807-2.8889 0.425l1.237 1.2362c0.5358-0.10587 1.0883-0.16119 1.6519-0.16119 3.9231 0 7.3099 2.6803 8.2471 6.4332 0.1004 0.4018 0.5075 0.6462 0.9094 0.5459 0.4019-0.1004 0.6463-0.5075 0.5459-0.9094-1.103-4.417-5.0869-7.5697-9.7024-7.5697zm0.1947 3.5093 3.8013 3.8007c-0.1018-2.0569-1.7488-3.7024-3.8013-3.8007z</StreamGeometry>
   </Styles.Resources>
+
   <Style Selector="TextBox">
     <Setter Property="Foreground" Value="{DynamicResource TextControlForeground}" />
     <Setter Property="Background" Value="{DynamicResource TextControlBackground}" />
@@ -22,12 +27,13 @@
     <Setter Property="MinHeight" Value="{DynamicResource TextControlThemeMinHeight}" />
     <Setter Property="MinWidth" Value="{DynamicResource TextControlThemeMinWidth}" />
     <Setter Property="Padding" Value="{DynamicResource TextControlThemePadding}" />
+    <Setter Property="FocusAdorner" Value="{x:Null}" />
     <Setter Property="Template">
       <ControlTemplate>
         <DockPanel>
 
           <!-- TODO bind Content -> Header and ContentTemplate -> HeaderTemplate -->
-          <ContentPresenter x:Name="HeaderContentPresenter"
+          <ContentPresenter x:Name="PART_HeaderContentPresenter"
                             DockPanel.Dock="Top"
                             TextBlock.FontWeight="Normal"
                             TextBlock.Foreground="{DynamicResource TextControlHeaderForeground}"
@@ -36,7 +42,7 @@
 
           <Panel>
             <Border
-              Name="border"
+              Name="PART_BorderElement"
               Background="{TemplateBinding Background}"
               BorderBrush="{TemplateBinding BorderBrush}"
               BorderThickness="{TemplateBinding BorderThickness}"
@@ -46,57 +52,50 @@
             </Border>
 
             <Border
-              Padding="{TemplateBinding Padding}"
               Margin="{TemplateBinding BorderThickness}">
-              <DockPanel>
-                <TextBlock Name="floatingWatermark"
-                           Foreground="{DynamicResource SystemAccentColor}"
-                           FontSize="{TemplateBinding FontSize}"
-                           Text="{TemplateBinding Watermark}"
-                           DockPanel.Dock="Top">
-                  <TextBlock.IsVisible>
-                      <MultiBinding Converter="{x:Static BoolConverters.And}">
-                        <Binding RelativeSource="{RelativeSource TemplatedParent}"
-                               Path="UseFloatingWatermark"/>
-                        <Binding RelativeSource="{RelativeSource TemplatedParent}"
-                               Path="Text"
-                               Converter="{x:Static StringConverters.IsNotNullOrEmpty}"/>
-                      </MultiBinding>
-                  </TextBlock.IsVisible>
-                </TextBlock>
-
-                <DataValidationErrors>
-                  <ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
-                                VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
-
-                    <Panel>
-                      <TextBlock Name="watermark"
-                               Opacity="0.5"
-                               Text="{TemplateBinding Watermark}"
-                               TextAlignment="{TemplateBinding TextAlignment}"
-                               TextWrapping="{TemplateBinding TextWrapping}"
-                               IsVisible="{TemplateBinding Text, Converter={x:Static StringConverters.IsNullOrEmpty}}"
-                               HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
-                               VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
-                      <!-- TODO eliminate this margin... text layout issue? -->
-                      <TextPresenter Name="PART_TextPresenter"
-                                   Margin="0 1 0 0"
-                                   Text="{TemplateBinding Text, Mode=TwoWay}"
-                                   CaretIndex="{TemplateBinding CaretIndex}"
-                                   SelectionStart="{TemplateBinding SelectionStart}"
-                                   SelectionEnd="{TemplateBinding SelectionEnd}"
-                                   TextAlignment="{TemplateBinding TextAlignment}"
-                                   TextWrapping="{TemplateBinding TextWrapping}"
-                                   PasswordChar="{TemplateBinding PasswordChar}"
-                                   SelectionBrush="{TemplateBinding SelectionBrush}"
-                                   SelectionForegroundBrush="{TemplateBinding SelectionForegroundBrush}"
-                                   CaretBrush="{TemplateBinding CaretBrush}"
-                                   HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
-                                   VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> 
-                    </Panel>
-                  </ScrollViewer>
-                </DataValidationErrors>
-              </DockPanel>
+              <Grid ColumnDefinitions="Auto,*,Auto" >
+                <ContentPresenter Grid.Column="0" Grid.ColumnSpan="1" Content="{TemplateBinding InnerLeftContent}"/>
+                <DockPanel x:Name="PART_InnerDockPanel" Grid.Column="1" Grid.ColumnSpan="1" Margin="{TemplateBinding Padding}">
+                  <TextBlock Name="PART_FloatingWatermark"
+                             Foreground="{DynamicResource SystemAccentColor}"
+                             FontSize="{TemplateBinding FontSize}"
+                             Text="{TemplateBinding Watermark}"
+                             DockPanel.Dock="Top" />
+
+                  <DataValidationErrors>
+                    <ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
+                                  VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
+                      <Panel>
+                        <TextBlock Name="PART_Watermark"
+                                Opacity="0.5"
+                                Text="{TemplateBinding Watermark}"
+                                TextAlignment="{TemplateBinding TextAlignment}"
+                                TextWrapping="{TemplateBinding TextWrapping}"
+                                IsVisible="{TemplateBinding Text, Converter={x:Static StringConverters.IsNullOrEmpty}}"
+                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
+                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
+                        <!-- TODO eliminate this margin... text layout issue? -->
+                        <TextPresenter Name="PART_TextPresenter"
+                                      Margin="0 1 0 0"
+                                      Text="{TemplateBinding Text, Mode=TwoWay}"
+                                      CaretIndex="{TemplateBinding CaretIndex}"
+                                      SelectionStart="{TemplateBinding SelectionStart}"
+                                      SelectionEnd="{TemplateBinding SelectionEnd}"
+                                      TextAlignment="{TemplateBinding TextAlignment}"
+                                      TextWrapping="{TemplateBinding TextWrapping}"
+                                      PasswordChar="{TemplateBinding PasswordChar}"
+                                      RevealPassword="{TemplateBinding RevealPassword}"
+                                      SelectionBrush="{TemplateBinding SelectionBrush}"
+                                      SelectionForegroundBrush="{TemplateBinding SelectionForegroundBrush}"
+                                      CaretBrush="{TemplateBinding CaretBrush}"
+                                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
+                                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> 
+                      </Panel>
+                    </ScrollViewer>
+                  </DataValidationErrors>
+                </DockPanel>
+                <ContentPresenter Grid.Column="2" Grid.ColumnSpan="1" Content="{TemplateBinding InnerRightContent}"/>
+              </Grid>
             </Border>
           </Panel>
         </DockPanel>
@@ -109,12 +108,12 @@
     <Setter Property="Foreground" Value="{DynamicResource TextControlForegroundDisabled}" />
   </Style>
 
-  <Style Selector="TextBox:disabled /template/ Border#border">
+  <Style Selector="TextBox:disabled /template/ Border#PART_BorderElement">
     <Setter Property="Background" Value="{DynamicResource TextControlBackgroundDisabled}" />
     <Setter Property="BorderBrush" Value="{DynamicResource TextControlBorderBrushDisabled}" />
   </Style>
 
-  <Style Selector="TextBox:disabled /template/ TextBlock#watermark, TextBox:disabled /template/ TextBlock#floatingWatermark">
+  <Style Selector="TextBox:disabled /template/ TextBlock#PART_Watermark, TextBox:disabled /template/ TextBlock#PART_FloatingWatermark">
     <Setter Property="Foreground" Value="{DynamicResource TextControlPlaceholderForegroundDisabled}" />
   </Style>
 
@@ -123,12 +122,12 @@
     <Setter Property="Foreground" Value="{DynamicResource TextControlForegroundPointerOver}" />
   </Style>
 
-  <Style Selector="TextBox:pointerover /template/ Border#border">
+  <Style Selector="TextBox:pointerover /template/ Border#PART_BorderElement">
     <Setter Property="BorderBrush" Value="{DynamicResource TextControlBorderBrushPointerOver}"/>
     <Setter Property="Background" Value="{DynamicResource TextControlBackgroundPointerOver}" />
   </Style>
 
-  <Style Selector="TextBox:pointerover /template/ TextBlock#watermark, TextBox:pointerover /template/ TextBlock#floatingWatermark">
+  <Style Selector="TextBox:pointerover /template/ TextBlock#PART_Watermark, TextBox:pointerover /template/ TextBlock#PART_FloatingWatermark">
     <Setter Property="Foreground" Value="{DynamicResource TextControlPlaceholderForegroundPointerOver}" />
   </Style>
 
@@ -137,23 +136,152 @@
     <Setter Property="Foreground" Value="{DynamicResource TextControlForegroundFocused}" />
   </Style>
 
-  <Style Selector="TextBox:focus /template/ TextBlock#watermark, TextBox:focus /template/ TextBlock#floatingWatermark">
+  <Style Selector="TextBox:focus /template/ TextBlock#PART_Watermark, TextBox:focus /template/ TextBlock#PART_FloatingWatermark">
     <Setter Property="Foreground" Value="{DynamicResource TextControlPlaceholderForegroundFocused}" />
   </Style>
 
-  <Style Selector="TextBox:focus /template/ Border#border">
+  <Style Selector="TextBox:focus /template/ Border#PART_BorderElement">
     <Setter Property="Background" Value="{DynamicResource TextControlBackgroundFocused}"/>
     <Setter Property="BorderBrush" Value="{DynamicResource TextControlBorderBrushFocused}"/>
     <Setter Property="BorderThickness" Value="{DynamicResource TextControlBorderThemeThicknessFocused}" />
   </Style>
 
-
-  <Style Selector="TextBox:error /template/ Border#border">
+  <Style Selector="TextBox:error /template/ Border#PART_BorderElement">
     <Setter Property="BorderBrush" Value="{DynamicResource SystemControlErrorTextForegroundBrush}"/>
   </Style>
 
-  <Style Selector="TextBox /template/ DockPanel">
+  <Style Selector="TextBox /template/ DockPanel#PART_InnerDockPanel">
     <Setter Property="Cursor" Value="IBeam" />
   </Style>
+  
+  <Style Selector="TextBox /template/ TextBlock#PART_FloatingWatermark">
+    <Setter Property="IsVisible" Value="False" />
+  </Style>
+  <Style Selector="TextBox[UseFloatingWatermark=true]:not(TextBox:empty) /template/ TextBlock#PART_FloatingWatermark">
+    <Setter Property="IsVisible" Value="True" />
+  </Style>
+
+  <Style Selector="TextBox.revealPasswordButton[AcceptsReturn=False][IsReadOnly=False]:not(TextBox:empty)">
+    <Setter Property="InnerRightContent">
+      <Template>
+        <ToggleButton Classes="PasswordBoxRevealButton"
+                      IsChecked="{Binding $parent[TextBox].RevealPassword, Mode=TwoWay}" />
+      </Template>
+    </Setter>
+  </Style>
+
+  <Style Selector="TextBox.clearButton[AcceptsReturn=False][IsReadOnly=False]:focus:not(TextBox:empty)">
+    <Setter Property="InnerRightContent">
+      <Template>
+        <Button Classes="TextBoxClearButton"
+                Command="{Binding $parent[TextBox].Clear}" />
+      </Template>
+    </Setter>
+  </Style>
+
+  <Style Selector="ToggleButton.PasswordBoxRevealButton">
+    <Setter Property="Template">
+      <Setter.Value>
+        <ControlTemplate TargetType="ToggleButton">
+          <Border x:Name="PART_ButtonLayoutBorder"
+                  BorderThickness="{TemplateBinding BorderThickness}">
+            <Panel>
+              <Path x:Name="PART_GlyphElement_Reveal"
+                    Data="{StaticResource PasswordBoxRevealButtonData}"
+                    Height="8"
+                    Width="12"
+                    Stretch="Uniform"
+                    VerticalAlignment="Center"
+                    HorizontalAlignment="Center" />
+              <Path x:Name="PART_GlyphElement_Hide"
+                    Data="{StaticResource PasswordBoxHideButtonData}"
+                    Height="12"
+                    Width="12"
+                    Stretch="Uniform"
+                    VerticalAlignment="Center"
+                    HorizontalAlignment="Center" />
+            </Panel>
+          </Border>
+        </ControlTemplate>
+      </Setter.Value>
+    </Setter>
+  </Style>
+
+  <Style Selector="Button.TextBoxClearButton">
+    <Setter Property="Template">
+      <Setter.Value>
+        <ControlTemplate TargetType="Button">
+          <Border x:Name="PART_ButtonLayoutBorder"
+                  BorderThickness="{TemplateBinding BorderThickness}">
+            <Path x:Name="PART_GlyphElement"
+                  Data="{StaticResource TextBoxClearButtonData}"
+                  Height="10"
+                  Width="10"
+                  Stretch="Uniform"
+                  VerticalAlignment="Center"
+                  HorizontalAlignment="Center"/>
+          </Border>
+        </ControlTemplate>
+      </Setter.Value>
+    </Setter>
+  </Style>
+
+  <!-- TextBox.Button Normal State -->
+  <Style Selector="Button.TextBoxClearButton, ToggleButton.PasswordBoxRevealButton">
+    <Setter Property="MinWidth" Value="34" />
+    <Setter Property="Width" Value="{Binding $self.Bounds.Height}"/>
+    <Setter Property="Focusable" Value="False" />
+    <Setter Property="VerticalAlignment" Value="Stretch" />    
+  </Style>
+  <Style Selector="Button.TextBoxClearButton /template/ Border#PART_ButtonLayoutBorder,
+                   ToggleButton.PasswordBoxRevealButton /template/ Border#PART_ButtonLayoutBorder">
+    <Setter Property="Background" Value="{DynamicResource TextControlButtonBackground}" />
+    <Setter Property="BorderBrush" Value="{DynamicResource TextControlButtonBorderBrush}" />
+  </Style>
+  <Style Selector="Button.TextBoxClearButton /template/ Path#PART_GlyphElement,
+                   ToggleButton.PasswordBoxRevealButton /template/ Path#PART_GlyphElement_Reveal,
+                   ToggleButton.PasswordBoxRevealButton /template/ Path#PART_GlyphElement_Hide">
+    <Setter Property="Fill" Value="{DynamicResource TextControlButtonForeground}" />
+  </Style>
+
+  <!-- TextBox.Button PointerOver State -->
+  <Style Selector="Button.TextBoxClearButton:pointerover /template/ Border#PART_ButtonLayoutBorder,
+                   ToggleButton.PasswordBoxRevealButton:pointerover /template/ Border#PART_ButtonLayoutBorder">
+    <Setter Property="Background" Value="{DynamicResource TextControlButtonBackgroundPointerOver}" />
+    <Setter Property="BorderBrush" Value="{DynamicResource TextControlButtonBorderBrushPointerOver}" />
+  </Style>
+  <Style Selector="Button.TextBoxClearButton:pointerover /template/ Path#PART_GlyphElement,
+                   ToggleButton.PasswordBoxRevealButton:pointerover /template/ Path#PART_GlyphElement_Reveal,
+                   ToggleButton.PasswordBoxRevealButton:pointerover /template/ Path#PART_GlyphElement_Hide">
+    <Setter Property="Fill" Value="{DynamicResource TextControlButtonForegroundPointerOver}" />
+  </Style>
+
+  <!-- TextBox.Button Pressed State -->
+  <Style Selector="Button.TextBoxClearButton:pressed /template/ Border#PART_ButtonLayoutBorder,
+                   ToggleButton.PasswordBoxRevealButton:pressed /template/ Border#PART_ButtonLayoutBorder,
+                   ToggleButton.PasswordBoxRevealButton:checked /template/ Border#PART_ButtonLayoutBorder,
+                   ToggleButton.PasswordBoxRevealButton:indeterminate /template/ Border#PART_ButtonLayoutBorder">
+    <Setter Property="Background" Value="{DynamicResource TextControlButtonBackgroundPressed}" />
+    <Setter Property="BorderBrush" Value="{DynamicResource TextControlButtonBorderBrushPressed}" />
+  </Style>
+  <Style Selector="Button.TextBoxClearButton:pressed /template/ Path#PART_GlyphElement,
+                   ToggleButton.PasswordBoxRevealButton:pressed /template/ Path#PART_GlyphElement_Reveal,
+                   ToggleButton.PasswordBoxRevealButton:checked /template/ Path#PART_GlyphElement_Hide,
+                   ToggleButton.PasswordBoxRevealButton:indeterminate /template/ Path#PART_GlyphElement_Reveal">
+    <Setter Property="Fill" Value="{DynamicResource TextControlButtonForegroundPressed}" />
+  </Style>
+
+  <!-- TextBox.Button Disabled State -->
+  <Style Selector="Button.TextBoxClearButton:disabled /template/ Border#PART_ButtonLayoutBorder,
+                   ToggleButton.PasswordBoxRevealButton:disabled /template/ Border#PART_ButtonLayoutBorder">
+    <Setter Property="Opacity" Value="0" />
+  </Style>
+
+  <Style Selector="ToggleButton.PasswordBoxRevealButton:not(ToggleButton:checked) /template/ Path#PART_GlyphElement_Hide">
+    <Setter Property="IsVisible" Value="False" />
+  </Style>
+  <Style Selector="ToggleButton.PasswordBoxRevealButton:checked /template/ Path#PART_GlyphElement_Reveal">
+    <Setter Property="IsVisible" Value="False" />
+  </Style>
 
 </Styles>

+ 34 - 0
tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

@@ -521,6 +521,40 @@ namespace Avalonia.Controls.UnitTests
                 Assert.Equal(1, lfcount);
             }
         }
+        
+        [Fact]
+        public void TextBox_Reveal_Password_Reset_When_Lost_Focus()
+        {
+            using (UnitTestApplication.Start(FocusServices))
+            {
+                var target1 = new TextBox
+                {
+                    Template = CreateTemplate(),
+                    Text = "1234",
+                    PasswordChar = '*'
+                };
+                var target2 = new TextBox
+                {
+                    Template = CreateTemplate(),
+                    Text = "5678"
+                };
+                var sp = new StackPanel();
+                sp.Children.Add(target1);
+                sp.Children.Add(target2);
+
+                target1.ApplyTemplate();
+                target2.ApplyTemplate();
+                
+                var root = new TestRoot { Child = sp };
+
+                target1.Focus();
+                target1.RevealPassword = true;
+                
+                target2.Focus();
+                
+                Assert.False(target1.RevealPassword);
+            }
+        }
 
         [Fact]
         public void Setting_Bound_Text_To_Null_Works()