[CmdPal] Fixes for Exts: All Apps, System Commands, Time And Date (#38103)

<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request

All apps:
- Fix missing second command for opnening settings page

Time and Date plugin:
- fix for missing settings list in global plugin manager
- fix for wrong page title

system plugin
- fix for missing settings page in global plugin manager
- fix for missing open button on plugin list
- ~fix for wrong icon for firmware boot~
- fix for wrong subtitle for ipv6
- fix for wrong details for mac address
- fix for wrong layout of network details
- layout improvements for network details
- change default value for "hide disconected networks" to $false
- rename empty recycle bin setting to "Hide Empty Recycle Bin command"

![image](https://github.com/user-attachments/assets/fba608ca-3229-408e-9efb-596ead03ac19)

![image](https://github.com/user-attachments/assets/8b3a4ab1-499a-4e3c-8c2e-be19162d971b)

![image](https://github.com/user-attachments/assets/fd5d2a3c-6a9a-4990-a006-70646405d165)



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

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors already. If 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:
Heiko
2025-03-24 19:39:08 +01:00
committed by GitHub
parent a9a41ca1a2
commit 33cc612e40
12 changed files with 87 additions and 59 deletions

View File

@@ -22,7 +22,11 @@ public partial class AllAppsCommandProvider : CommandProvider
Icon = IconHelpers.FromRelativePath("Assets\\AllApps.svg");
Settings = AllAppsSettings.Instance.Settings;
_listItem = new(Page) { Subtitle = Resources.search_installed_apps };
_listItem = new(Page)
{
Subtitle = Resources.search_installed_apps,
MoreCommands = [new CommandContextItem(AllAppsSettings.Instance.Settings.SettingsPage)],
};
}
public override ICommandItem[] TopLevelCommands() => [_listItem];

View File

@@ -33,11 +33,11 @@ internal static class Commands
/// Returns a list with all system command results
/// </summary>
/// <param name="isUefi">Value indicating if the system is booted in uefi mode</param>
/// <param name="splitRecycleBinResults">Value indicating if we should show two results for Recycle Bin.</param>
/// <param name="hideEmptyRecycleBin">Value indicating if we should hide the Empty Recycle Bin command.</param>
/// <param name="confirmCommands">A value indicating if the user should confirm the system commands</param>
/// <param name="emptyRBSuccessMessage">Show a success message after empty Recycle Bin.</param>
/// <returns>A list of all results</returns>
public static List<IListItem> GetSystemCommands(bool isUefi, bool splitRecycleBinResults, bool confirmCommands, bool emptyRBSuccessMessage)
public static List<IListItem> GetSystemCommands(bool isUefi, bool hideEmptyRecycleBin, bool confirmCommands, bool emptyRBSuccessMessage)
{
var results = new List<IListItem>();
results.AddRange(new[]
@@ -81,7 +81,7 @@ internal static class Commands
});
// Show Recycle Bin results based on setting.
if (splitRecycleBinResults)
if (!hideEmptyRecycleBin)
{
results.AddRange(new[]
{
@@ -142,6 +142,7 @@ internal static class Commands
}
CompositeFormat sysIpv4DescriptionCompositeFormate = CompositeFormat.Parse(Resources.Microsoft_plugin_sys_ip4_description);
CompositeFormat sysIpv6DescriptionCompositeFormate = CompositeFormat.Parse(Resources.Microsoft_plugin_sys_ip6_description);
CompositeFormat sysMacDescriptionCompositeFormate = CompositeFormat.Parse(Resources.Microsoft_plugin_sys_mac_description);
var hideDisconnectedNetworkInfo = manager.HideDisconnectedNetworkInfo;
@@ -171,7 +172,7 @@ internal static class Commands
results.Add(new ListItem(new CopyTextCommand(intInfo.GetConnectionDetails()))
{
Title = intInfo.IPv6Primary,
Subtitle = string.Format(CultureInfo.InvariantCulture, sysIpv4DescriptionCompositeFormate, intInfo.ConnectionName),
Subtitle = string.Format(CultureInfo.InvariantCulture, sysIpv6DescriptionCompositeFormate, intInfo.ConnectionName),
Icon = Icons.NetworkAdapterIcon,
Details = new Details() { Title = Resources.Microsoft_plugin_ext_connection_details, Body = intInfo.GetConnectionDetails() },
});
@@ -184,7 +185,7 @@ internal static class Commands
Title = intInfo.PhysicalAddress,
Subtitle = string.Format(CultureInfo.InvariantCulture, sysMacDescriptionCompositeFormate, intInfo.Adapter, intInfo.ConnectionName),
Icon = Icons.NetworkAdapterIcon,
Details = new Details() { Title = Resources.Microsoft_plugin_ext_connection_details, Body = intInfo.GetConnectionDetails() },
Details = new Details() { Title = Resources.Microsoft_plugin_ext_adapter_details, Body = intInfo.GetAdapterDetails() },
});
}
}
@@ -203,12 +204,12 @@ internal static class Commands
var isBootedInUefiMode = Win32Helpers.GetSystemFirmwareType() == FirmwareType.Uefi;
var separateEmptyRB = manager.ShowSeparateResultForEmptyRecycleBin;
var hideEmptyRB = manager.HideEmptyRecycleBin;
var confirmSystemCommands = manager.ShowDialogToConfirmCommand;
var showSuccessOnEmptyRB = manager.ShowSuccessMessageAfterEmptyingRecycleBin;
// normal system commands are fast and can be returned immediately
var systemCommands = Commands.GetSystemCommands(isBootedInUefiMode, separateEmptyRB, confirmSystemCommands, showSuccessOnEmptyRB);
var systemCommands = Commands.GetSystemCommands(isBootedInUefiMode, hideEmptyRB, confirmSystemCommands, showSuccessOnEmptyRB);
list.AddRange(systemCommands);
list.AddRange(networkConnectionResults);

View File

@@ -19,6 +19,14 @@ namespace Microsoft.CmdPal.Ext.System.Helpers;
/// </summary>
internal sealed class NetworkConnectionProperties
{
/// <summary>
/// Decimal unicode value for green circle emoji.
/// We need to generate it in the code because it does not render using Markdown emoji syntax or Unicode character syntax.
/// </summary>
/// <seealso cref="https://github.com/CommunityToolkit/Labs-Windows/blob/main/components/MarkdownTextBlock/samples/MarkdownTextBlock.md"/>
/// <seealso cref="https://github.com/xoofx/markdig/blob/master/src/Markdig/Extensions/Emoji/EmojiMapping.cs"/>
private const int GreenCircleCharacter = 128994;
/// <summary>
/// Gets the name of the adapter
/// </summary>
@@ -161,12 +169,12 @@ internal sealed class NetworkConnectionProperties
/// <returns>String with the details</returns>
internal string GetAdapterDetails()
{
return $"{Resources.Microsoft_plugin_sys_AdapterName}: {Adapter}" +
$"\n{Resources.Microsoft_plugin_sys_PhysicalAddress}: {PhysicalAddress}" +
$"\n{Resources.Microsoft_plugin_sys_Speed}: {GetFormattedSpeedValue(Speed)}" +
$"\n{Resources.Microsoft_plugin_sys_Type}: {GetAdapterTypeAsString(Type)}" +
$"\n{Resources.Microsoft_plugin_sys_State}: " + (State == OperationalStatus.Up ? Resources.Microsoft_plugin_sys_Connected : Resources.Microsoft_plugin_sys_Disconnected) +
$"\n{Resources.Microsoft_plugin_sys_ConnectionName}: {ConnectionName}";
return $"**{Resources.Microsoft_plugin_sys_AdapterName}:** {Adapter}" +
$"\n\n**{Resources.Microsoft_plugin_sys_State}:** " + (State == OperationalStatus.Up ? char.ConvertFromUtf32(GreenCircleCharacter) + " " + Resources.Microsoft_plugin_sys_Connected : ":red_circle: " + Resources.Microsoft_plugin_sys_Disconnected) +
$"\n\n**{Resources.Microsoft_plugin_sys_PhysicalAddress}:** {PhysicalAddress}" +
$"\n\n**{Resources.Microsoft_plugin_sys_Speed}:** {GetFormattedSpeedValue(Speed)}" +
$"\n\n**{Resources.Microsoft_plugin_sys_Type}:** {GetAdapterTypeAsString(Type)}" +
$"\n\n**{Resources.Microsoft_plugin_sys_ConnectionName}:** {ConnectionName}";
}
/// <summary>
@@ -175,24 +183,24 @@ internal sealed class NetworkConnectionProperties
/// <returns>String with the details</returns>
internal string GetConnectionDetails()
{
return $"{Resources.Microsoft_plugin_sys_ConnectionName}: {ConnectionName}" +
$"\n{Resources.Microsoft_plugin_sys_State}: " + (State == OperationalStatus.Up ? Resources.Microsoft_plugin_sys_Connected : Resources.Microsoft_plugin_sys_Disconnected) +
$"\n{Resources.Microsoft_plugin_sys_Type}: {GetAdapterTypeAsString(Type)}" +
$"\n{Resources.Microsoft_plugin_sys_Suffix}: {Suffix}" +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip4Address}: ", IPv4) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip4SubnetMask}: ", IPv4Mask) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Address}:\n\t", IPv6Global) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Temp}:\n\t", IPv6Temporary) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Link}:\n\t", IPv6LinkLocal) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Site}:\n\t", IPv6SiteLocal) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Unique}:\n\t", IPv6UniqueLocal) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Gateways}:\n\t", Gateways) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Dhcp}:\n\t", DhcpServers == null ? string.Empty : DhcpServers) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Dns}:\n\t", DnsServers == null ? string.Empty : DnsServers) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Wins}:\n\t", WinsServers == null ? string.Empty : WinsServers) +
$"\n\n{Resources.Microsoft_plugin_sys_AdapterName}: {Adapter}" +
$"\n{Resources.Microsoft_plugin_sys_PhysicalAddress}: {PhysicalAddress}" +
$"\n{Resources.Microsoft_plugin_sys_Speed}: {GetFormattedSpeedValue(Speed)}";
return $"**{Resources.Microsoft_plugin_sys_ConnectionName}:** {ConnectionName}" +
$"\n\n**{Resources.Microsoft_plugin_sys_State}:** " + (State == OperationalStatus.Up ? char.ConvertFromUtf32(GreenCircleCharacter) + " " + Resources.Microsoft_plugin_sys_Connected : ":red_circle: " + Resources.Microsoft_plugin_sys_Disconnected) +
$"\n\n**{Resources.Microsoft_plugin_sys_Type}:** {GetAdapterTypeAsString(Type)}" +
$"\n\n**{Resources.Microsoft_plugin_sys_Suffix}:** {Suffix}" +
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Ip4Address}:** ", IPv4) +
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Ip4SubnetMask}:** ", IPv4Mask) +
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Ip6Address}:**\n\n* ", IPv6Global) +
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Ip6Temp}:**\n\n* ", IPv6Temporary) +
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Ip6Link}:**\n\n* ", IPv6LinkLocal) +
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Ip6Site}:**\n\n* ", IPv6SiteLocal) +
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Ip6Unique}:**\n\n* ", IPv6UniqueLocal) +
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Gateways}:**\n\n* ", Gateways) +
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Dhcp}:**\n\n* ", DhcpServers == null ? string.Empty : DhcpServers) +
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Dns}:**\n\n* ", DnsServers == null ? string.Empty : DnsServers) +
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Wins}:**\n\n* ", WinsServers == null ? string.Empty : WinsServers) +
$"\n\n**{Resources.Microsoft_plugin_sys_AdapterName}:** {Adapter}" +
$"\n\n**{Resources.Microsoft_plugin_sys_PhysicalAddress}:** {PhysicalAddress}" +
$"\n\n**{Resources.Microsoft_plugin_sys_Speed}:** {GetFormattedSpeedValue(Speed)}";
}
/// <summary>
@@ -304,13 +312,13 @@ internal sealed class NetworkConnectionProperties
switch (property)
{
case string:
return $"\n{title}{property}";
return string.IsNullOrWhiteSpace(property) ? string.Empty : $"\n\n{title}{property}";
case List<string> listString:
return listString.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
return listString.Count == 0 ? string.Empty : $"\n\n{title}{string.Join("\n\n* ", property)}";
case List<IPAddress> listIP:
return listIP.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
return listIP.Count == 0 ? string.Empty : $"\n\n{title}{string.Join("\n\n* ", property)}";
case IPAddressCollection collectionIP:
return collectionIP.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
return collectionIP.Count == 0 ? string.Empty : $"\n\n{title}{string.Join("\n\n* ", property)}";
case null:
return string.Empty;
default:

View File

@@ -25,23 +25,23 @@ public class SettingsManager : JsonSettingsManager
Resources.Microsoft_plugin_sys_RecycleBin_ShowEmptySuccessMessage,
false); // TODO -- double check default value
private readonly ToggleSetting _showSeparateResultForEmptyRecycleBin = new(
Namespaced(nameof(ShowSeparateResultForEmptyRecycleBin)),
Resources.Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate,
Resources.Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate,
true); // TODO -- double check default value
private readonly ToggleSetting _hideEmptyRecycleBin = new(
Namespaced(nameof(HideEmptyRecycleBin)),
Resources.Microsoft_plugin_sys_RecycleBin_HideEmpty,
Resources.Microsoft_plugin_sys_RecycleBin_HideEmpty,
false);
private readonly ToggleSetting _hideDisconnectedNetworkInfo = new(
Namespaced(nameof(HideDisconnectedNetworkInfo)),
Resources.Microsoft_plugin_ext_settings_hideDisconnectedNetworkInfo,
Resources.Microsoft_plugin_ext_settings_hideDisconnectedNetworkInfo,
true); // TODO -- double check default value
false);
public bool ShowDialogToConfirmCommand => _showDialogToConfirmCommand.Value;
public bool ShowSuccessMessageAfterEmptyingRecycleBin => _showSuccessMessageAfterEmptyingRecycleBin.Value;
public bool ShowSeparateResultForEmptyRecycleBin => _showSeparateResultForEmptyRecycleBin.Value;
public bool HideEmptyRecycleBin => _hideEmptyRecycleBin.Value;
public bool HideDisconnectedNetworkInfo => _hideDisconnectedNetworkInfo.Value;
@@ -60,7 +60,7 @@ public class SettingsManager : JsonSettingsManager
Settings.Add(_showDialogToConfirmCommand);
Settings.Add(_showSuccessMessageAfterEmptyingRecycleBin);
Settings.Add(_showSeparateResultForEmptyRecycleBin);
Settings.Add(_hideEmptyRecycleBin);
Settings.Add(_hideDisconnectedNetworkInfo);
// Load settings from file upon initialization

View File

@@ -15,6 +15,7 @@ public sealed partial class SystemCommandPage : ListPage
public SystemCommandPage(SettingsManager settingsManager)
{
Title = Resources.Microsoft_plugin_ext_system_page_name;
Name = Resources.Microsoft_plugin_ext_system_page_name;
Icon = IconHelpers.FromRelativePath("Assets\\SystemCommand.svg");
_settingsManager = settingsManager;
ShowDetails = true;

View File

@@ -159,6 +159,15 @@ namespace Microsoft.CmdPal.Ext.System {
}
}
/// <summary>
/// Looks up a localized string similar to Adapter Details.
/// </summary>
public static string Microsoft_plugin_ext_adapter_details {
get {
return ResourceManager.GetString("Microsoft_plugin_ext_adapter_details", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Connection Details.
/// </summary>
@@ -528,6 +537,15 @@ namespace Microsoft.CmdPal.Ext.System {
}
}
/// <summary>
/// Looks up a localized string similar to Hide the Empty Recycle Bin command.
/// </summary>
public static string Microsoft_plugin_sys_RecycleBin_HideEmpty {
get {
return ResourceManager.GetString("Microsoft_plugin_sys_RecycleBin_HideEmpty", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Recycle Bin is empty..
/// </summary>
@@ -546,15 +564,6 @@ namespace Microsoft.CmdPal.Ext.System {
}
}
/// <summary>
/// Looks up a localized string similar to Show separate result for Empty Recycle Bin command.
/// </summary>
public static string Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate {
get {
return ResourceManager.GetString("Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Show a success message after emptying the Recycle Bin.
/// </summary>

View File

@@ -151,6 +151,9 @@
<data name="Microsoft_plugin_ext_connection_details" xml:space="preserve">
<value>Connection Details</value>
</data>
<data name="Microsoft_plugin_ext_adapter_details" xml:space="preserve">
<value>Adapter Details</value>
</data>
<data name="Microsoft_plugin_ext_copy" xml:space="preserve">
<value>Copy to clipboard</value>
</data>
@@ -309,8 +312,8 @@
<value>Empty Recycle Bin</value>
<comment>This should align to the action in Windows of emptying the recycle bin on your computer.</comment>
</data>
<data name="Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate" xml:space="preserve">
<value>Show separate result for Empty Recycle Bin command</value>
<data name="Microsoft_plugin_sys_RecycleBin_HideEmpty" xml:space="preserve">
<value>Hide the Empty Recycle Bin command</value>
</data>
<data name="Microsoft_plugin_sys_RecycleBin_ShowEmptySuccessMessage" xml:space="preserve">
<value>Show a success message after emptying the Recycle Bin</value>

View File

@@ -22,13 +22,14 @@ public partial class SystemCommandExtensionProvider : CommandProvider
_commands = [
new CommandItem(Page)
{
Title = DisplayName,
Title = Resources.Microsoft_plugin_ext_system_page_name,
Icon = Page.Icon,
MoreCommands = [new CommandContextItem(_settingsManager.Settings.SettingsPage)],
},
];
Icon = Page.Icon;
Settings = _settingsManager.Settings;
}
public override ICommandItem[] TopLevelCommands()

View File

@@ -20,7 +20,7 @@ public sealed partial class SystemCommandsCache
{
var isBootedInUefiMode = Win32Helpers.GetSystemFirmwareType() == FirmwareType.Uefi;
var separateEmptyRB = manager.ShowSeparateResultForEmptyRecycleBin;
var separateEmptyRB = manager.HideEmptyRecycleBin;
var confirmSystemCommands = manager.ShowDialogToConfirmCommand;
var showSuccessOnEmptyRB = manager.ShowSuccessMessageAfterEmptyingRecycleBin;

View File

@@ -268,7 +268,7 @@ namespace Microsoft.CmdPal.Ext.TimeDate {
}
/// <summary>
/// Looks up a localized string similar to Time &amp;amp; Date.
/// Looks up a localized string similar to Time and Date.
/// </summary>
public static string Microsoft_plugin_timedate_main_page_title {
get {

View File

@@ -370,7 +370,7 @@
<value>Error: Invalid input</value>
</data>
<data name="Microsoft_plugin_timedate_main_page_title" xml:space="preserve">
<value>Time &amp;amp; Date</value>
<value>Time and Date</value>
</data>
<data name="Microsoft_plugin_timedate_InvalidInput_ErrorMessageSubTitle" xml:space="preserve">
<value>Valid prefixes: 'u' for Unix Timestamp, 'ums' for Unix Timestamp in milliseconds, 'ft' for Windows file time</value>

View File

@@ -32,6 +32,7 @@ public partial class TimeDateCommandsProvider : CommandProvider
};
Icon = _timeDateExtensionPage.Icon;
Settings = _settingsManager.Settings;
}
private string GetTranslatedPluginDescription()