From cba8b06834d6656ba8ce2eb32cf06f49542ec0b5 Mon Sep 17 00:00:00 2001 From: CreeperAWA Date: Tue, 3 Nov 2026 01:13:20 +0800 Subject: [PATCH] =?UTF-8?q?fix(controls):=20MultiComboBox=20=E6=8A=AC?= =?UTF-8?q?=E8=B5=B7=E6=97=B6=E6=89=8D=E5=B1=95=E5=BC=80=E4=B8=8B=E6=8B=89?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8D=E8=A7=A6=E6=91=B8=E5=B1=8F=E4=B8=8B?= =?UTF-8?q?=E6=8B=89=E6=A1=86=E4=B8=80=E8=A7=A6=E5=8D=B3=E6=94=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SecRandom.Core/Controls/MultiComboBox.cs | 53 ++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/SecRandom.Core/Controls/MultiComboBox.cs b/SecRandom.Core/Controls/MultiComboBox.cs index 5e7a4910..849239f5 100644 --- a/SecRandom.Core/Controls/MultiComboBox.cs +++ b/SecRandom.Core/Controls/MultiComboBox.cs @@ -5,10 +5,14 @@ using Avalonia; using Avalonia.Collections; using Avalonia.Controls; +using Avalonia.Controls.Platform; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; +using Avalonia.Controls.Utils; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.Media; +using Avalonia.VisualTree; namespace SecRandom.Core.Controls; @@ -18,6 +22,7 @@ public class MultiComboBox : ItemsControl public const string PART_SelectionBox = "PART_SelectionBox"; public const string PC_DropDownOpen = ":dropdownopen"; public const string PC_Empty = ":selection-empty"; + public const string PC_Pressed = ":pressed"; private static readonly ITemplate _defaultPanel = new FuncTemplate(() => new VirtualizingStackPanel()); @@ -43,6 +48,7 @@ public class MultiComboBox : ItemsControl private INotifyCollectionChanged? _subscribedSelection; private bool _selectionViewDirty; private bool _syncingSelectionView; + private Popup? _popup; public static readonly StyledProperty IsDropDownOpenProperty = ComboBox.IsDropDownOpenProperty.AddOwner(); @@ -69,6 +75,7 @@ static MultiComboBox() { FocusableProperty.OverrideDefaultValue(true); ItemsPanelProperty.OverrideDefaultValue(_defaultPanel); + PlatformFeedback.FeedbackTypeProperty.OverrideDefaultValue(FeedbackType.Auto); SelectedItemsProperty.Changed.AddClassHandler((box, args) => box.OnSelectedItemsChanged(args)); } @@ -77,7 +84,6 @@ public MultiComboBox() { SetCurrentValue(SelectedItemsProperty, new AvaloniaList()); RemoveCommand = new MultiComboBoxRemoveCommand(this); - AddHandler(PointerPressedEvent, OnBackgroundPointerPressed); } public bool IsDropDownOpen @@ -174,6 +180,7 @@ protected override void PrepareContainerForItemOverride(Control container, objec protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); + _popup = e.NameScope.Find("PART_Popup"); var selectionBox = e.NameScope.Find(PART_SelectionBox); if (selectionBox is null) @@ -190,9 +197,49 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang PseudoClasses.Set(PC_DropDownOpen, IsDropDownOpen); } - private void OnBackgroundPointerPressed(object? sender, PointerPressedEventArgs e) + /// + /// 与 Avalonia 相同的指针节奏:按下只进入按压态(:pressed), + /// 抬起才切换下拉。按下瞬间展开下拉时,同一次触摸的抬起仍会落在刚出现的下拉层上, + /// 轻触关闭把这次手势当作下拉框之外的按压,下拉框随即收起,用户点不到里面的复选框; + /// 改为抬起展开后,打开手势已经结束,这条收起路径不存在。 + /// + protected override void OnPointerPressed(PointerPressedEventArgs e) { - SetCurrentValue(IsDropDownOpenProperty, !IsDropDownOpen); + base.OnPointerPressed(e); + if (!e.Handled && e.Source is Visual source) + { + if (_popup?.IsInsidePopup(source) == true) + { + e.Handled = true; + return; + } + } + + if (IsDropDownOpen) + { + // 下拉展开时按下控件视为关闭,而不是再切回打开 + SetCurrentValue(IsDropDownOpenProperty, false); + e.Handled = true; + } + else + { + PseudoClasses.Set(PC_Pressed, true); + } + } + + protected override void OnPointerReleased(PointerReleasedEventArgs e) + { + if (!e.Handled && e.Source is Visual source + && _popup?.IsInsidePopup(source) != true + && PseudoClasses.Contains(PC_Pressed)) + { + SetCurrentValue(IsDropDownOpenProperty, !IsDropDownOpen); + this.PerformFeedback(FeedbackAction.Click); + e.Handled = true; + } + + PseudoClasses.Set(PC_Pressed, false); + base.OnPointerReleased(e); } protected override void OnKeyDown(KeyEventArgs e)