CmdPal: Make Window Walker Close window command respect "Keep open" option (#45721)

## Summary of the Pull Request

This PR fixes the "Close window" command in the Window Walker built-in
extension and makes it obey the "Keep open after closing window" option
in the extension's settings.

It also adds a message that forces Window Walker to refresh its list of
windows, so that the closed window disappears from the list (and
similarly for end task commands).

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #43256 
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
This commit is contained in:
Jiří Polášek
2026-03-28 00:46:36 +01:00
committed by GitHub
parent 4cb3359314
commit 5792d32d32
8 changed files with 72 additions and 15 deletions

View File

@@ -53,7 +53,7 @@ public class Settings : ISettingsInterface
public bool KillProcessTree => killProcessTree;
public bool OpenAfterKillAndClose => openAfterKillAndClose;
public bool KeepOpenAfterKillAndClose => openAfterKillAndClose;
public bool HideKillProcessOnElevatedProcesses => hideKillProcessOnElevatedProcesses;

View File

@@ -2,7 +2,11 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Ext.WindowWalker.Components;
using Microsoft.CmdPal.Ext.WindowWalker.Helpers;
using Microsoft.CmdPal.Ext.WindowWalker.Messages;
using Microsoft.CmdPal.Ext.WindowWalker.Properties;
using Microsoft.CommandPalette.Extensions;
namespace Microsoft.CmdPal.Ext.WindowWalker.Commands;
@@ -26,6 +30,7 @@ internal sealed partial class CloseWindowCommand : InvokableCommand
}
_window.CloseThisWindow();
return CommandResult.Dismiss();
WeakReferenceMessenger.Default.Send(new RefreshWindowsMessage());
return SettingsManager.Instance.KeepOpenAfterKillAndClose ? CommandResult.KeepOpen() : CommandResult.Dismiss();
}
}

View File

@@ -2,7 +2,12 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Ext.WindowWalker.Components;
using Microsoft.CmdPal.Ext.WindowWalker.Helpers;
using Microsoft.CmdPal.Ext.WindowWalker.Messages;
using Microsoft.CmdPal.Ext.WindowWalker.Properties;
using Microsoft.CommandPalette.Extensions;
namespace Microsoft.CmdPal.Ext.WindowWalker.Commands;
@@ -57,16 +62,13 @@ internal sealed partial class EndTaskCommand : InvokableCommand
// Kill process
window.Process.KillThisProcess(SettingsManager.Instance.KillProcessTree);
return !SettingsManager.Instance.OpenAfterKillAndClose;
return !SettingsManager.Instance.KeepOpenAfterKillAndClose;
}
public override ICommandResult Invoke()
{
if (KillProcess(_window))
{
return CommandResult.Dismiss();
}
return CommandResult.KeepOpen();
WeakReferenceMessenger.Default.Send(new RefreshWindowsMessage());
var shouldHide = KillProcess(_window);
return shouldHide ? CommandResult.Dismiss() : CommandResult.KeepOpen();
}
}

View File

@@ -16,7 +16,7 @@ public interface ISettingsInterface
public bool KillProcessTree { get; }
public bool OpenAfterKillAndClose { get; }
public bool KeepOpenAfterKillAndClose { get; }
public bool HideKillProcessOnElevatedProcesses { get; }

View File

@@ -45,7 +45,7 @@ public class SettingsManager : JsonSettingsManager, ISettingsInterface
false);
private readonly ToggleSetting _openAfterKillAndClose = new(
Namespaced(nameof(OpenAfterKillAndClose)),
Namespaced(nameof(KeepOpenAfterKillAndClose)),
Resources.windowwalker_SettingOpenAfterKillAndClose,
Resources.windowwalker_SettingOpenAfterKillAndClose_Description,
false);
@@ -84,7 +84,7 @@ public class SettingsManager : JsonSettingsManager, ISettingsInterface
public bool KillProcessTree => _killProcessTree.Value;
public bool OpenAfterKillAndClose => _openAfterKillAndClose.Value;
public bool KeepOpenAfterKillAndClose => _openAfterKillAndClose.Value;
public bool HideKillProcessOnElevatedProcesses => _hideKillProcessOnElevatedProcesses.Value;

View File

@@ -0,0 +1,7 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CmdPal.Ext.WindowWalker.Messages;
public record RefreshWindowsMessage(bool Delay = true);

View File

@@ -18,6 +18,9 @@
<ProjectReference Include="..\..\..\..\common\ManagedCsWin32\ManagedCsWin32.csproj" />
<!-- WASDK, WebView2, CmdPal Toolkit references now included via Common.ExtDependencies.props -->
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DependentUpon>Resources.resx</DependentUpon>

View File

@@ -2,12 +2,18 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Ext.WindowWalker.Components;
using Microsoft.CmdPal.Ext.WindowWalker.Helpers;
using Microsoft.CmdPal.Ext.WindowWalker.Messages;
using Microsoft.CmdPal.Ext.WindowWalker.Properties;
using Microsoft.CommandPalette.Extensions;
namespace Microsoft.CmdPal.Ext.WindowWalker.Pages;
internal sealed partial class WindowWalkerListPage : DynamicListPage, IDisposable
internal sealed partial class WindowWalkerListPage : DynamicListPage, IDisposable, IRecipient<RefreshWindowsMessage>
{
private System.Threading.CancellationTokenSource _cancellationTokenSource = new();
@@ -26,6 +32,8 @@ internal sealed partial class WindowWalkerListPage : DynamicListPage, IDisposabl
Title = Resources.window_walker_top_level_command_title,
Subtitle = Resources.windowwalker_NoResultsMessage,
};
WeakReferenceMessenger.Default.Register<RefreshWindowsMessage>(this);
}
public override void UpdateSearchText(string oldSearch, string newSearch)
@@ -75,19 +83,51 @@ internal sealed partial class WindowWalkerListPage : DynamicListPage, IDisposabl
public override IListItem[] GetItems() => Query(SearchText);
public void Receive(RefreshWindowsMessage message)
{
if (_disposed)
{
return;
}
if (!message.Delay)
{
Refresh();
}
else
{
Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(3)).ConfigureAwait(false);
Refresh();
});
}
}
private void Refresh()
{
if (_disposed)
{
return;
}
RaiseItemsChanged();
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
private void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_cancellationTokenSource.Dispose();
WeakReferenceMessenger.Default.UnregisterAll(this);
_cancellationTokenSource?.Dispose();
_disposed = true;
}
}