Files
PowerToys/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/AliasManager.cs
Jiří Polášek 753689309e CmdPal: Fix alias UI clearing when toggling Direct/Indirect combobox (#45381)
## Summary of the Pull Request

This PR fixes the settings UI for the top-level command alias.



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


https://github.com/user-attachments/assets/0d1e1392-0293-4482-97cb-e8e8c0ed0dd5


- [x] Closes: #41301
<!-- - [ ] 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
2026-02-05 16:47:45 -06:00

135 lines
4.3 KiB
C#

// 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.
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Core.ViewModels.Messages;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class AliasManager : ObservableObject
{
private readonly TopLevelCommandManager _topLevelCommandManager;
// REMEMBER, CommandAlias.SearchPrefix is what we use as keys
private readonly Dictionary<string, CommandAlias> _aliases;
public AliasManager(TopLevelCommandManager tlcManager, SettingsModel settings)
{
_topLevelCommandManager = tlcManager;
_aliases = settings.Aliases;
if (_aliases.Count == 0)
{
PopulateDefaultAliases();
}
}
private void AddAlias(CommandAlias a) => _aliases.Add(a.SearchPrefix, a);
public bool CheckAlias(string searchText)
{
if (_aliases.TryGetValue(searchText, out var alias))
{
try
{
var topLevelCommand = _topLevelCommandManager.LookupCommand(alias.CommandId);
if (topLevelCommand is not null)
{
WeakReferenceMessenger.Default.Send<ClearSearchMessage>();
WeakReferenceMessenger.Default.Send<PerformCommandMessage>(topLevelCommand.GetPerformCommandMessage());
return true;
}
}
catch
{
}
}
return false;
}
private void PopulateDefaultAliases()
{
this.AddAlias(new CommandAlias(":", "com.microsoft.cmdpal.registry", true));
this.AddAlias(new CommandAlias("$", "com.microsoft.cmdpal.windowsSettings", true));
this.AddAlias(new CommandAlias("=", "com.microsoft.cmdpal.calculator", true));
this.AddAlias(new CommandAlias(">", "com.microsoft.cmdpal.shell", true));
this.AddAlias(new CommandAlias("<", "com.microsoft.cmdpal.windowwalker", true));
this.AddAlias(new CommandAlias("??", "com.microsoft.cmdpal.websearch", true));
this.AddAlias(new CommandAlias("file", "com.microsoft.indexer.fileSearch", false));
this.AddAlias(new CommandAlias(")", "com.microsoft.cmdpal.timedate", true));
}
public string? KeysFromId(string commandId)
{
return _aliases
.Where(kv => kv.Value.CommandId == commandId)
.Select(kv => kv.Value.Alias)
.FirstOrDefault();
}
public CommandAlias? AliasFromId(string commandId)
{
return _aliases
.Where(kv => kv.Value.CommandId == commandId)
.Select(kv => kv.Value)
.FirstOrDefault();
}
public void UpdateAlias(string commandId, CommandAlias? newAlias)
{
if (string.IsNullOrEmpty(commandId))
{
// do nothing?
return;
}
// If we already have _this exact alias_, do nothing
if (newAlias is not null &&
_aliases.TryGetValue(newAlias.SearchPrefix, out var existingAlias))
{
if (existingAlias.CommandId == commandId)
{
return;
}
}
List<CommandAlias> toRemove = [];
foreach (var kv in _aliases)
{
// Look for the old aliases for the command, and remove it
if (kv.Value.CommandId == commandId)
{
toRemove.Add(kv.Value);
}
// Look for the alias belonging to another command, and remove it
if (newAlias is not null && kv.Value.Alias == newAlias.Alias && kv.Value.CommandId != commandId)
{
toRemove.Add(kv.Value);
// Remove alias from other TopLevelViewModels it may be assigned to
var topLevelCommand = _topLevelCommandManager.LookupCommand(kv.Value.CommandId);
if (topLevelCommand is not null)
{
topLevelCommand.AliasText = string.Empty;
}
}
}
foreach (var alias in toRemove)
{
// REMEMBER, SearchPrefix is what we use as keys
_aliases.Remove(alias.SearchPrefix);
}
if (newAlias is not null)
{
AddAlias(newAlias);
}
}
}