Browse Source

Focus search pane on Ctrl+Shift+F / Ctrl+E

DockWorkspace gains ShowToolPane(string contentId) — walks the
layout's IDockable graph, matches on Id, calls SetActiveDockable +
SetFocusedDockable. ShowSearchCommand wraps it with the search
pane's content id; bound to two MainWindow.KeyBindings entries
(Ctrl+Shift+F mirrors the WPF default, Ctrl+E mirrors VS's "search
box" gesture).

Assisted-by: Claude:claude-opus-4-7:Claude Code
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
39303559f1
  1. 62
      ILSpy.Tests/Search/ShowSearchCommandTests.cs
  2. 43
      ILSpy/Docking/DockWorkspace.cs
  3. 4
      ILSpy/Views/MainWindow.axaml

62
ILSpy.Tests/Search/ShowSearchCommandTests.cs

@ -0,0 +1,62 @@ @@ -0,0 +1,62 @@
// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Threading.Tasks;
using Avalonia.Headless.NUnit;
using AwesomeAssertions;
using ILSpy.AppEnv;
using ILSpy.Docking;
using ILSpy.Search;
using ILSpy.ViewModels;
using ILSpy.Views;
using NUnit.Framework;
namespace ICSharpCode.ILSpy.Tests.Search;
[TestFixture]
public class ShowSearchCommandTests
{
[AvaloniaTest]
public async Task ShowSearchCommand_Activates_The_Search_Pane()
{
// Ctrl+Shift+F / Ctrl+E in the live app are wired to this command via
// MainWindow.KeyBindings. Here we drive the command directly and verify the
// activation reaches the SearchPaneModel.
var window = AppComposition.Current.GetExport<MainWindow>();
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1);
var dockWorkspace = AppComposition.Current.GetExport<DockWorkspace>();
var search = AppComposition.Current.GetExport<SearchPaneModel>();
dockWorkspace.ShowSearchCommand.CanExecute(null).Should().BeTrue();
dockWorkspace.ShowSearchCommand.Execute(null);
// The dock factory's ActiveDockable should now be the search pane. We can't
// assert against the factory's deep state easily (IDockable identity), so check
// the pane's IsActive flag, which Dock.Avalonia flips during SetActiveDockable.
search.IsActive.Should().BeTrue(
"ShowSearchCommand must bring the search pane to the active tab in its dock group");
}
}

43
ILSpy/Docking/DockWorkspace.cs

@ -71,6 +71,12 @@ namespace ILSpy.Docking @@ -71,6 +71,12 @@ namespace ILSpy.Docking
public IRelayCommand NavigateForwardCommand { get; }
public IRelayCommand<NavigationEntry> NavigateToHistoryCommand { get; }
/// <summary>
/// Brings the search tool pane to focus. Wired to Ctrl+Shift+F and Ctrl+E on the
/// main window. Idempotent — re-firing on an already-active pane is a no-op.
/// </summary>
public IRelayCommand ShowSearchCommand { get; }
// Read-only history snapshots for the Back/Forward split-button dropdowns; oldest-first.
// The UI reverses these for newest-first display.
public IReadOnlyList<NavigationEntry> BackHistory => history.BackEntries;
@ -93,6 +99,8 @@ namespace ILSpy.Docking @@ -93,6 +99,8 @@ namespace ILSpy.Docking
NavigateForwardCommand = new RelayCommand(NavigateForward, () => history.CanNavigateForward);
NavigateToHistoryCommand = new RelayCommand<NavigationEntry>(NavigateToHistory,
entry => entry != null && (history.BackEntries.Contains(entry) || history.ForwardEntries.Contains(entry)));
ShowSearchCommand = new RelayCommand(
() => ShowToolPane(ILSpy.Search.SearchPaneModel.PaneContentId));
using (ILSpy.AppEnv.StartupLog.Phase("ILSpyDockFactory ctor + CreateLayout"))
{
factory = new ILSpyDockFactory(toolPaneRegistry);
@ -555,6 +563,41 @@ namespace ILSpy.Docking @@ -555,6 +563,41 @@ namespace ILSpy.Docking
/// so <see cref="OnActiveDockableChanged"/> can pick it up and pull the tree's
/// selection across — setting it after-the-fact misses the activation event.
/// </param>
/// <summary>
/// Brings the tool pane with the given <paramref name="contentId"/> to focus. Walks
/// every <see cref="IDockable"/> in the layout via <see cref="GetAllDockables"/> and
/// matches on <see cref="IDockable.Id"/>. Silently no-ops when the pane isn't in the
/// layout — e.g. it was closed via its X button and hasn't been re-added.
/// </summary>
public void ShowToolPane(string contentId)
{
foreach (var dockable in GetAllDockables(Layout))
{
if (dockable is ToolPaneModel toolPane && toolPane.Id == contentId)
{
factory.SetActiveDockable(toolPane);
if (toolPane.Owner is IDock owner)
factory.SetFocusedDockable(owner, toolPane);
return;
}
}
}
static System.Collections.Generic.IEnumerable<IDockable> GetAllDockables(IDockable? root)
{
if (root == null)
yield break;
yield return root;
if (root is IDock dock && dock.VisibleDockables != null)
{
foreach (var child in dock.VisibleDockables)
{
foreach (var descendant in GetAllDockables(child))
yield return descendant;
}
}
}
public ContentTabPage OpenNewTab(object content, SharpTreeNode? sourceNode = null)
{
var tab = new ContentTabPage { Content = content, SourceNode = sourceNode };

4
ILSpy/Views/MainWindow.axaml

@ -23,6 +23,10 @@ @@ -23,6 +23,10 @@
Avalonia 12 yet, so we wire keyboard only for now. -->
<KeyBinding Gesture="Alt+Left" Command="{Binding DockWorkspace.NavigateBackCommand}" />
<KeyBinding Gesture="Alt+Right" Command="{Binding DockWorkspace.NavigateForwardCommand}" />
<!-- Search pane shortcuts. Ctrl+Shift+F is the WPF default; Ctrl+E mirrors the
"search box" gesture VS uses. Both bring the bottom search pane to focus. -->
<KeyBinding Gesture="Ctrl+Shift+F" Command="{Binding DockWorkspace.ShowSearchCommand}" />
<KeyBinding Gesture="Ctrl+E" Command="{Binding DockWorkspace.ShowSearchCommand}" />
</Window.KeyBindings>
<DockPanel>

Loading…
Cancel
Save