diff --git a/ILSpy.Tests/Analyzers/AnalyzerTreeKeyboardTests.cs b/ILSpy.Tests/Analyzers/AnalyzerTreeKeyboardTests.cs index 40d7b56b9..662c54b4d 100644 --- a/ILSpy.Tests/Analyzers/AnalyzerTreeKeyboardTests.cs +++ b/ILSpy.Tests/Analyzers/AnalyzerTreeKeyboardTests.cs @@ -70,6 +70,39 @@ public class AnalyzerTreeKeyboardTests description: "Right must expand the node via SharpTreeView.OnKeyDown on the analyzer tree"); } + [AvaloniaTest] + public async Task Enter_Activates_The_Selected_Analyzer_Node() + { + // Enter on a single selected analyzer row activates it -- for an entity node that means + // navigating to the member's home in the assembly tree, like 10.x did. The key must reach + // SharpTreeView.OnKeyDown: the container is a ListBoxItem, and Avalonia's default key + // selection triggers treat Enter/Space as selection input and mark the event handled + // before it bubbles, so SharpTreeView suppresses that trigger for the activation case. + var (window, vm) = await TestHarness.BootAsync(3); + var dockWorkspace = AppComposition.Current.GetExport(); + var analyzerVm = AppComposition.Current.GetExport(); + + var typeNode = vm.AssemblyTreeModel.FindNode( + "System.Linq", "System.Linq", "System.Linq.Enumerable"); + var entity = (ITypeDefinition)typeNode.Member!; + var analyzed = analyzerVm.Analyze(entity); + + dockWorkspace.ShowToolPane(AnalyzerTreeViewModel.PaneContentId); + var view = await window.WaitForComponent(); + var tree = await view.WaitForComponent(); + tree.SelectedItem = analyzed; + Dispatcher.UIThread.RunJobs(); + tree.FocusNode(analyzed); + Dispatcher.UIThread.RunJobs(); + + ((object?)vm.AssemblyTreeModel.SelectedItem).Should().NotBeSameAs(typeNode, + "precondition: the assembly tree must not already sit on the target node"); + + window.KeyPress(Key.Enter, RawInputModifiers.None, PhysicalKey.Enter, null); + await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, typeNode), + description: "Enter must activate the analyzer node and select the type in the assembly tree"); + } + [AvaloniaTest] public async Task Ctrl_R_Analyzes_The_Selected_Member() { diff --git a/ILSpy/Controls/TreeView/SharpTreeView.cs b/ILSpy/Controls/TreeView/SharpTreeView.cs index 83ddbfbeb..1babaf41a 100644 --- a/ILSpy/Controls/TreeView/SharpTreeView.cs +++ b/ILSpy/Controls/TreeView/SharpTreeView.cs @@ -295,6 +295,27 @@ namespace ICSharpCode.ILSpy.Controls.TreeView scrollViewer.Offset = new Vector(scrollViewer.Offset.X, newOffsetY); } + /// + /// Avalonia's default key selection triggers treat plain Enter/Space as selection input: + /// the ListBoxItem container marks the KeyDown handled before it bubbles here, so the + /// activation handling in would never see those keys. Suppress the + /// selection trigger exactly for the case OnKeyDown activates instead -- a single selected + /// row that is the row the key landed on. Multi-row selections keep the default behaviour + /// (Enter/Space collapses the selection to the focused row). + /// + protected override bool ShouldTriggerSelection(Visual selectable, KeyEventArgs eventArgs) + { + if (eventArgs.KeyModifiers == KeyModifiers.None + && eventArgs.Key is Key.Enter or Key.Space + && selectable is SharpTreeViewItem { Node: { } node } + && SelectedItems?.Count == 1 + && ReferenceEquals(SelectedItem, node)) + { + return false; + } + return base.ShouldTriggerSelection(selectable, eventArgs); + } + protected override void OnKeyDown(KeyEventArgs e) { // Ctrl+A select-all must work on the first press even before a current item is