Bläddra i källkod

Improve NumericUpDown and AutoCompleteBox focusability (#13376)

* Delegate focus to TextBox

* Port FocusChanged from AutoCompleteBox to NumericUpDown

* Improve focus with NumericUpDown and ButtonSpinner bindings

---------

Co-authored-by: Max Katz <[email protected]>
Magnus Lindhe 1 år sedan
förälder
incheckning
c2798ce4ad

+ 3 - 29
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs

@@ -463,6 +463,7 @@ namespace Avalonia.Controls
         static AutoCompleteBox()
         {
             FocusableProperty.OverrideDefaultValue<AutoCompleteBox>(true);
+            IsTabStopProperty.OverrideDefaultValue<AutoCompleteBox>(false);
 
             MinimumPopulateDelayProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnMinimumPopulateDelayChanged(e));
             IsDropDownOpenProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnIsDropDownOpenChanged(e));
@@ -770,33 +771,7 @@ namespace Avalonia.Controls
         /// <returns>true to indicate the
         /// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> has focus;
         /// otherwise, false.</returns>
-        protected bool HasFocus()
-        {
-            Visual? focused = FocusManager.GetFocusManager(this)?.GetFocusedElement() as Visual;
-
-            while (focused != null)
-            {
-                if (object.ReferenceEquals(focused, this))
-                {
-                    return true;
-                }
-
-                // This helps deal with popups that may not be in the same
-                // visual tree
-                Visual? parent = focused.GetVisualParent();
-                if (parent == null)
-                {
-                    // Try the logical parent.
-                    Control? element = focused as Control;
-                    if (element != null)
-                    {
-                        parent = element.VisualParent;
-                    }
-                }
-                focused = parent;
-            }
-            return false;
-        }
+        protected bool HasFocus() => IsKeyboardFocusWithin;
 
         /// <summary>
         /// Handles the FocusChanged event.
@@ -820,8 +795,7 @@ namespace Avalonia.Controls
                 if (!wasFocused && TextBox != null && TextBoxSelectionLength <= 0)
                 {
                     TextBox.Focus();
-                    TextBox.SelectionStart = 0;
-                    TextBox.SelectionEnd = TextBox.Text?.Length ?? 0;
+                    TextBox.SelectAll();
                 }
             }
             else

+ 34 - 0
src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs

@@ -137,6 +137,7 @@ namespace Avalonia.Controls
         private bool _internalValueSet;
         private bool _isSyncingTextAndValueProperties;
         private bool _isTextChangedFromUI;
+        private bool _isFocused;
 
         /// <summary>
         /// Gets the Spinner template part.
@@ -344,6 +345,16 @@ namespace Avalonia.Controls
             TextProperty.Changed.Subscribe(OnTextChanged);
             TextConverterProperty.Changed.Subscribe(OnTextConverterChanged);
             ValueProperty.Changed.Subscribe(OnValueChanged);
+
+            FocusableProperty.OverrideDefaultValue<NumericUpDown>(true);
+            IsTabStopProperty.OverrideDefaultValue<NumericUpDown>(false);
+        }
+
+        /// <inheritdoc />
+        protected override void OnGotFocus(GotFocusEventArgs e)
+        {
+            base.OnGotFocus(e);
+            FocusChanged(IsKeyboardFocusWithin);
         }
 
         /// <inheritdoc />
@@ -351,6 +362,7 @@ namespace Avalonia.Controls
         {
             CommitInput(true);
             base.OnLostFocus(e);
+            FocusChanged(IsKeyboardFocusWithin);
         }
 
         /// <inheritdoc />
@@ -1164,5 +1176,27 @@ namespace Avalonia.Controls
             }
             return false;
         }
+
+        private void FocusChanged(bool hasFocus)
+        {
+            // The OnGotFocus & OnLostFocus are asynchronously and cannot
+            // reliably tell you that have the focus.  All they do is let you
+            // know that the focus changed sometime in the past.  To determine
+            // if you currently have the focus you need to do consult the
+            // FocusManager.
+
+            bool wasFocused = _isFocused;
+            _isFocused = hasFocus;
+
+            if (hasFocus)
+            {
+
+                if (!wasFocused && TextBox != null)
+                {
+                    TextBox.Focus();
+                    TextBox.SelectAll();
+                }
+            }
+        }
     }
 }

+ 4 - 0
src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml

@@ -92,10 +92,12 @@
                   MinHeight="{TemplateBinding MinHeight}">
             <DockPanel>
               <StackPanel Name="PART_SpinnerPanel"
+                          TabIndex="2"
                           DockPanel.Dock="Right"
                           Orientation="Horizontal"
                           IsVisible="{TemplateBinding ShowButtonSpinner}">
                 <RepeatButton Name="PART_IncreaseButton"
+                              IsTabStop="{TemplateBinding IsTabStop}"
                               Theme="{StaticResource FluentButtonSpinnerRepeatButton}"
                               Background="{TemplateBinding Background}"
                               BorderBrush="{TemplateBinding BorderBrush}"
@@ -110,6 +112,7 @@
                 </RepeatButton>
 
                 <RepeatButton Name="PART_DecreaseButton"
+                              IsTabStop="{TemplateBinding IsTabStop}"
                               Theme="{StaticResource FluentButtonSpinnerRepeatButton}"
                               Background="{TemplateBinding Background}"
                               BorderBrush="{TemplateBinding BorderBrush}"
@@ -126,6 +129,7 @@
 
               <ContentPresenter Name="PART_ContentPresenter"
                                 Grid.Column="1"
+                                TabIndex="1"
                                 ContentTemplate="{TemplateBinding ContentTemplate}"
                                 Content="{TemplateBinding Content}"
                                 HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"

+ 1 - 0
src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml

@@ -35,6 +35,7 @@
                        BorderThickness="{TemplateBinding BorderThickness}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        CornerRadius="{TemplateBinding CornerRadius}"
+                       IsTabStop="False"
                        Padding="0"
                        MinWidth="0"
                        HorizontalContentAlignment="Stretch"

+ 4 - 0
src/Avalonia.Themes.Simple/Controls/ButtonSpinner.xaml

@@ -34,10 +34,12 @@
                   CornerRadius="{TemplateBinding CornerRadius}">
             <DockPanel>
               <UniformGrid Name="PART_SpinnerPanel"
+                           TabIndex="2"
                            DockPanel.Dock="Right"
                            IsVisible="{TemplateBinding ShowButtonSpinner}"
                            Rows="2">
                 <RepeatButton Name="PART_IncreaseButton"
+                              IsTabStop="{TemplateBinding IsTabStop}"
                               Theme="{StaticResource SimpleButtonSpinnerRepeatButton}">
                   <Path Width="8"
                         Height="4"
@@ -48,6 +50,7 @@
                         Stretch="Uniform" />
                 </RepeatButton>
                 <RepeatButton Name="PART_DecreaseButton"
+                              IsTabStop="{TemplateBinding IsTabStop}"
                               Theme="{StaticResource SimpleButtonSpinnerRepeatButton}">
                   <Path Width="8"
                         Height="4"
@@ -60,6 +63,7 @@
               </UniformGrid>
               <ContentPresenter Name="PART_ContentPresenter"
                                 Grid.Column="1"
+                                TabIndex="1"
                                 Padding="{TemplateBinding Padding}"
                                 HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                 VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"

+ 1 - 0
src/Avalonia.Themes.Simple/Controls/NumericUpDown.xaml

@@ -12,6 +12,7 @@
         <ButtonSpinner Name="PART_Spinner"
                        HorizontalContentAlignment="Stretch"
                        VerticalContentAlignment="Stretch"
+                       IsTabStop="False"
                        AllowSpin="{TemplateBinding AllowSpin}"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"