Improve AutoCompleteBox focus behavior for 11.3.x (#761)
* feat: improve AutoCompleteBox focus behavior for 11.3.x * fix: open dropdown on tab navigation regardless of navigation method * feat: update per copilot comment. * feat: add a flag to control focus. * fix: fix naming.
This commit is contained in:
@@ -14,11 +14,18 @@
|
||||
Name="PART_TextBox"
|
||||
MinHeight="{TemplateBinding MinHeight}"
|
||||
VerticalAlignment="Stretch"
|
||||
iri:ClassHelper.ClassSource="{TemplateBinding}"
|
||||
DataValidationErrors.Errors="{TemplateBinding (DataValidationErrors.Errors)}"
|
||||
InnerLeftContent="{TemplateBinding InnerLeftContent}"
|
||||
InnerRightContent="{TemplateBinding InnerRightContent}"
|
||||
Watermark="{TemplateBinding Watermark}" />
|
||||
<Button
|
||||
Name="PART_ClearButton"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="{DynamicResource TextBoxContentPadding}"
|
||||
Command="{Binding $parent[u:AutoCompleteBox].Clear}"
|
||||
Content="{DynamicResource IconButtonClearData}"
|
||||
IsVisible="False"
|
||||
Theme="{StaticResource InnerIconButton}" />
|
||||
<Popup
|
||||
Name="PART_Popup"
|
||||
MaxHeight="{TemplateBinding MaxDropDownHeight}"
|
||||
@@ -45,5 +52,13 @@
|
||||
</Panel>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
<Style Selector="^.clearButton, ^.ClearButton">
|
||||
<Style Selector="^:focus-within:not(:empty) /template/ Button#PART_ClearButton">
|
||||
<Setter Property="IsVisible" Value="True" />
|
||||
</Style>
|
||||
<Style Selector="^:pointerover:not(:empty) /template/ Button#PART_ClearButton">
|
||||
<Setter Property="IsVisible" Value="True" />
|
||||
</Style>
|
||||
</Style>
|
||||
</ControlTheme>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.VisualTree;
|
||||
using Irihi.Avalonia.Shared.Common;
|
||||
using Irihi.Avalonia.Shared.Contracts;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
public class AutoCompleteBox : Avalonia.Controls.AutoCompleteBox, IClearControl
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private const string PART_TextBox = "PART_TextBox";
|
||||
private bool _closeBySelectionFlag;
|
||||
|
||||
private TextBox? _textbox;
|
||||
static AutoCompleteBox()
|
||||
{
|
||||
MinimumPrefixLengthProperty.OverrideDefaultValue<AutoCompleteBox>(0);
|
||||
@@ -14,18 +24,43 @@ public class AutoCompleteBox : Avalonia.Controls.AutoCompleteBox, IClearControl
|
||||
|
||||
public AutoCompleteBox()
|
||||
{
|
||||
AddHandler(PointerPressedEvent, OnBoxPointerPressed, RoutingStrategies.Tunnel);
|
||||
AddHandler(PointerReleasedEvent, OnCurrentPointerReleased, RoutingStrategies.Tunnel);
|
||||
}
|
||||
|
||||
private void OnCurrentPointerReleased(object sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
var source = (e.Source as Control).FindAncestorOfType<ListBoxItem>();
|
||||
if (source is not null)
|
||||
{
|
||||
_closeBySelectionFlag = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
_textbox?.RemoveHandler(PointerPressedEvent, OnBoxPointerPressed);
|
||||
_textbox = e.NameScope.Find<TextBox>(PART_TextBox);
|
||||
_textbox?.AddHandler(PointerPressedEvent, OnBoxPointerPressed, handledEventsToo: true);
|
||||
PseudoClasses.Set(PseudoClassName.PC_Empty, string.IsNullOrEmpty(Text));
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
SetCurrentValue(SelectedItemProperty, null);
|
||||
// Note: this method only resets Text to null.
|
||||
// By default, AutoCompleteBox will clear the SelectedItem when Text is set to null.
|
||||
// But user can use custom Predicate to control the behavior when Text is set to null.
|
||||
SetCurrentValue(TextProperty, null);
|
||||
}
|
||||
|
||||
private void OnBoxPointerPressed(object? sender, PointerPressedEventArgs e)
|
||||
{
|
||||
if (Equals(sender, this) && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed && IsDropDownOpen == false)
|
||||
if (Equals(sender, _textbox)
|
||||
&& e.GetCurrentPoint(this).Properties.IsLeftButtonPressed
|
||||
&& IsDropDownOpen == false)
|
||||
{
|
||||
SetCurrentValue(IsDropDownOpenProperty, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnGotFocus(GotFocusEventArgs e)
|
||||
@@ -35,6 +70,22 @@ public class AutoCompleteBox : Avalonia.Controls.AutoCompleteBox, IClearControl
|
||||
if (e.NavigationMethod == NavigationMethod.Pointer) return;
|
||||
if (!this.GetTemplateChildren().Contains(e.Source)) return;
|
||||
// If the focus is set by keyboard navigation, open the dropdown.
|
||||
if (IsDropDownOpen == false) SetCurrentValue(IsDropDownOpenProperty, true);
|
||||
if (!_closeBySelectionFlag && IsDropDownOpen == false)
|
||||
{
|
||||
SetCurrentValue(IsDropDownOpenProperty, true);
|
||||
}
|
||||
_closeBySelectionFlag = false;
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
if (change.Property == TextProperty)
|
||||
{
|
||||
var value = change.GetNewValue<string?>();
|
||||
PseudoClasses.Set(PseudoClassName.PC_Empty, string.IsNullOrEmpty(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user