Skip to content

Commit 51becca

Browse files
authored
feat(tool-window): close other tool windows when opening one (#14)
When a tool window button is clicked to open, all other tool windows configured in the launch bar are automatically hidden first, providing Rider-like exclusive tool window behavior.
1 parent 83271b6 commit 51becca

2 files changed

Lines changed: 53 additions & 3 deletions

File tree

src/CodingWithCalvin.LaunchyBar/LaunchyBarPackage.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke
4747
{
4848
// Initialize services
4949
_configurationService = new ConfigurationService();
50-
_launchService = new LaunchService(this);
50+
_launchService = new LaunchService(this, _configurationService);
5151
_shellInjectionService = new ShellInjectionService(_configurationService, _launchService);
5252

5353
// Try injection immediately, then retry with increasing delays

src/CodingWithCalvin.LaunchyBar/Services/LaunchService.cs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Diagnostics;
4+
using System.Linq;
45
using System.Threading.Tasks;
56
using CodingWithCalvin.LaunchyBar.Models;
67
using CodingWithCalvin.Otel4Vsix;
@@ -18,6 +19,7 @@ namespace CodingWithCalvin.LaunchyBar.Services;
1819
public sealed class LaunchService : ILaunchService
1920
{
2021
private readonly AsyncPackage _package;
22+
private readonly IConfigurationService _configurationService;
2123

2224
/// <summary>
2325
/// Maps VS View commands to their tool window GUIDs for toggle support.
@@ -37,9 +39,10 @@ public sealed class LaunchService : ILaunchService
3739
{ "View.GitWindow", new Guid("1c64b9c2-e352-428e-a56d-0ace190b99a6") },
3840
};
3941

40-
public LaunchService(AsyncPackage package)
42+
public LaunchService(AsyncPackage package, IConfigurationService configurationService)
4143
{
4244
_package = package;
45+
_configurationService = configurationService;
4346
}
4447

4548
/// <inheritdoc/>
@@ -147,10 +150,57 @@ private async Task ToggleToolWindowAsync(LaunchItem item)
147150
}
148151
}
149152

150-
// Window not found or hidden - show it via command
153+
// Window not found or hidden - hide other configured tool windows, then show this one
154+
await HideOtherToolWindowsAsync(item);
151155
await VS.Commands.ExecuteAsync(item.Target);
152156
}
153157

158+
private async Task HideOtherToolWindowsAsync(LaunchItem currentItem)
159+
{
160+
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
161+
162+
var shell = await _package.GetServiceAsync(typeof(SVsUIShell)) as IVsUIShell;
163+
if (shell == null) return;
164+
165+
// Collect GUIDs of other configured tool window items
166+
var otherGuids = _configurationService.Configuration.Items
167+
.Where(i => i.Type == LaunchItemType.ToolWindow
168+
&& !i.Target.Equals(currentItem.Target, StringComparison.OrdinalIgnoreCase))
169+
.Select(i => ToolWindowGuids.TryGetValue(i.Target, out var g) ? g : (Guid?)null)
170+
.Where(g => g.HasValue)
171+
.Select(g => g!.Value)
172+
.ToHashSet();
173+
174+
if (otherGuids.Count == 0) return;
175+
176+
shell.GetToolWindowEnum(out var windowEnum);
177+
if (windowEnum == null) return;
178+
179+
var frames = new IVsWindowFrame[1];
180+
while (windowEnum.Next(1, frames, out var fetched) == 0 && fetched == 1)
181+
{
182+
var frame = frames[0];
183+
if (frame == null) continue;
184+
185+
try
186+
{
187+
frame.GetGuidProperty((int)__VSFPROPID.VSFPROPID_GuidPersistenceSlot, out var persistGuid);
188+
if (otherGuids.Contains(persistGuid))
189+
{
190+
frame.IsOnScreen(out var isOnScreen);
191+
if (isOnScreen != 0)
192+
{
193+
frame.Hide();
194+
}
195+
}
196+
}
197+
catch
198+
{
199+
// Some frames may throw
200+
}
201+
}
202+
}
203+
154204
private async Task ToggleDebugAsync()
155205
{
156206
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

0 commit comments

Comments
 (0)