From e163d671abd86ec6a3807aeabd901c405a74081b Mon Sep 17 00:00:00 2001
From: Dong Bin <14807942+rabbitism@users.noreply.github.com>
Date: Wed, 10 Sep 2025 21:30:56 +0800
Subject: [PATCH] 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.
---
.../Controls/AutoCompleteBox.axaml | 17 +++++-
.../AutoCompleteBox/AutoCompleteBox.cs | 59 +++++++++++++++++--
2 files changed, 71 insertions(+), 5 deletions(-)
diff --git a/src/Ursa.Themes.Semi/Controls/AutoCompleteBox.axaml b/src/Ursa.Themes.Semi/Controls/AutoCompleteBox.axaml
index 5f56b87..facd2d0 100644
--- a/src/Ursa.Themes.Semi/Controls/AutoCompleteBox.axaml
+++ b/src/Ursa.Themes.Semi/Controls/AutoCompleteBox.axaml
@@ -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}" />
+
+
+
+
diff --git a/src/Ursa/Controls/AutoCompleteBox/AutoCompleteBox.cs b/src/Ursa/Controls/AutoCompleteBox/AutoCompleteBox.cs
index 17e7d13..115d58f 100644
--- a/src/Ursa/Controls/AutoCompleteBox/AutoCompleteBox.cs
+++ b/src/Ursa/Controls/AutoCompleteBox/AutoCompleteBox.cs
@@ -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(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();
+ if (source is not null)
+ {
+ _closeBySelectionFlag = true;
+ }
+ }
+
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ base.OnApplyTemplate(e);
+ _textbox?.RemoveHandler(PointerPressedEvent, OnBoxPointerPressed);
+ _textbox = e.NameScope.Find(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();
+ PseudoClasses.Set(PseudoClassName.PC_Empty, string.IsNullOrEmpty(value));
+ }
+ }
+
+
}
\ No newline at end of file