Browse Source

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 <maxkatz6@outlook.com>
release/11.0.8
Magnus Lindhe 2 years ago
committed by Max Katz
parent
commit
8d87c36901
  1. 32
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
  2. 34
      src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
  3. 4
      src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml
  4. 1
      src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml
  5. 4
      src/Avalonia.Themes.Simple/Controls/ButtonSpinner.xaml
  6. 1
      src/Avalonia.Themes.Simple/Controls/NumericUpDown.xaml

32
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
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
src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml

@ -93,10 +93,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}"
@ -112,6 +114,7 @@
</RepeatButton>
<RepeatButton Name="PART_DecreaseButton"
IsTabStop="{TemplateBinding IsTabStop}"
Theme="{StaticResource FluentButtonSpinnerRepeatButton}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
@ -129,6 +132,7 @@
<ContentPresenter Name="PART_ContentPresenter"
Grid.Column="1"
TabIndex="1"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"

1
src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml

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

4
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
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}"

Loading…
Cancel
Save