diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Commands/FancyZones/ApplyFancyZonesLayoutCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Commands/FancyZones/ApplyFancyZonesLayoutCommand.cs index b4ef1e55c9..7e4b4e6b93 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Commands/FancyZones/ApplyFancyZonesLayoutCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Commands/FancyZones/ApplyFancyZonesLayoutCommand.cs @@ -16,6 +16,7 @@ internal sealed partial class ApplyFancyZonesLayoutCommand : InvokableCommand { _layout = layout; _targetMonitor = monitor; + Id = FancyZonesCommandIds.BuildApplyLayoutCommandId(layout, monitor); Name = monitor is null ? "Apply to all monitors" : $"Apply to Monitor {monitor.Value.Title}"; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Helpers/FancyZonesCommandIds.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Helpers/FancyZonesCommandIds.cs new file mode 100644 index 0000000000..aa9b5c62b6 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Helpers/FancyZonesCommandIds.cs @@ -0,0 +1,92 @@ +// 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 System; + +namespace PowerToysExtension.Helpers; + +internal static class FancyZonesCommandIds +{ + private const string ApplyLayoutPrefix = "com.microsoft.powertoys.fancyzones.layout.apply:"; + private const string AllMonitorsSuffix = ":all"; + private const string MonitorMarker = ":monitor:"; + + public static string BuildApplyLayoutCommandId(FancyZonesLayoutDescriptor layout, FancyZonesMonitorDescriptor? monitor) + { + var escapedLayoutId = Uri.EscapeDataString(layout.Id); + return monitor is null + ? $"{ApplyLayoutPrefix}{escapedLayoutId}{AllMonitorsSuffix}" + : $"{ApplyLayoutPrefix}{escapedLayoutId}{MonitorMarker}{Uri.EscapeDataString(GetMonitorToken(monitor.Value))}"; + } + + public static bool TryParseApplyLayoutCommandId(string id, out string layoutId, out string? monitorToken) + { + layoutId = string.Empty; + monitorToken = null; + + if (string.IsNullOrWhiteSpace(id) || !id.StartsWith(ApplyLayoutPrefix, StringComparison.Ordinal)) + { + return false; + } + + var payload = id[ApplyLayoutPrefix.Length..]; + if (payload.EndsWith(AllMonitorsSuffix, StringComparison.Ordinal)) + { + var layoutPayload = payload[..^AllMonitorsSuffix.Length]; + if (string.IsNullOrWhiteSpace(layoutPayload)) + { + return false; + } + + try + { + layoutId = Uri.UnescapeDataString(layoutPayload); + } + catch (ArgumentException) + { + layoutId = string.Empty; + return false; + } + + return !string.IsNullOrWhiteSpace(layoutId); + } + + var monitorMarkerIndex = payload.IndexOf(MonitorMarker, StringComparison.Ordinal); + if (monitorMarkerIndex <= 0 || monitorMarkerIndex == payload.Length - MonitorMarker.Length) + { + return false; + } + + var layoutPart = payload[..monitorMarkerIndex]; + var monitorPart = payload[(monitorMarkerIndex + MonitorMarker.Length)..]; + if (string.IsNullOrWhiteSpace(layoutPart) || string.IsNullOrWhiteSpace(monitorPart)) + { + return false; + } + + try + { + layoutId = Uri.UnescapeDataString(layoutPart); + monitorToken = Uri.UnescapeDataString(monitorPart); + } + catch (ArgumentException) + { + layoutId = string.Empty; + monitorToken = null; + return false; + } + + return !string.IsNullOrWhiteSpace(layoutId) && !string.IsNullOrWhiteSpace(monitorToken); + } + + public static string GetMonitorToken(FancyZonesMonitorDescriptor monitor) + { + if (!string.IsNullOrWhiteSpace(monitor.Data.MonitorInstanceId)) + { + return $"instance:{monitor.Data.MonitorInstanceId}"; + } + + return $"fallback:{monitor.Data.Monitor}|{monitor.Data.MonitorNumber}"; + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Helpers/FancyZonesContextHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Helpers/FancyZonesContextHelper.cs new file mode 100644 index 0000000000..c1ea293bd7 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Helpers/FancyZonesContextHelper.cs @@ -0,0 +1,39 @@ +// 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 System.Collections.Generic; +using System.Globalization; +using System.Text; +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; +using PowerToysExtension.Commands; +using PowerToysExtension.Properties; + +namespace PowerToysExtension.Helpers; + +internal static class FancyZonesContextHelper +{ + private static readonly CompositeFormat ApplyToMonitorFormat = CompositeFormat.Parse(Resources.FancyZones_ApplyTo_Format); + + public static string FormatApplyToMonitorTitle(FancyZonesMonitorDescriptor monitor) + { + return string.Format(CultureInfo.CurrentCulture, ApplyToMonitorFormat, monitor.Title); + } + + public static IContextItem[] BuildLayoutContext(FancyZonesLayoutDescriptor layout, IReadOnlyList monitors) + { + var commands = new List(monitors.Count); + + foreach (var monitor in monitors) + { + commands.Add(new CommandContextItem(new ApplyFancyZonesLayoutCommand(layout, monitor)) + { + Title = FormatApplyToMonitorTitle(monitor), + Subtitle = monitor.Subtitle, + }); + } + + return commands.ToArray(); + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Pages/FancyZonesLayoutsPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Pages/FancyZonesLayoutsPage.cs index 897b7fa46a..6bd037726f 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Pages/FancyZonesLayoutsPage.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Pages/FancyZonesLayoutsPage.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Threading.Tasks; using Microsoft.CommandPalette.Extensions; @@ -70,7 +69,7 @@ internal sealed partial class FancyZonesLayoutsPage : DynamicListPage var item = new FancyZonesLayoutListItem(defaultCommand, layout, fallbackIcon) { - MoreCommands = BuildLayoutContext(layout, monitors), + MoreCommands = FancyZonesContextHelper.BuildLayoutContext(layout, monitors), }; items.Add(item); @@ -84,21 +83,4 @@ internal sealed partial class FancyZonesLayoutsPage : DynamicListPage return Array.Empty(); } } - - private static IContextItem[] BuildLayoutContext(FancyZonesLayoutDescriptor layout, IReadOnlyList monitors) - { - var commands = new List(monitors.Count); - - for (var i = 0; i < monitors.Count; i++) - { - var monitor = monitors[i]; - commands.Add(new CommandContextItem(new ApplyFancyZonesLayoutCommand(layout, monitor)) - { - Title = string.Format(CultureInfo.CurrentCulture, "Apply to {0}", monitor.Title), - Subtitle = monitor.Subtitle, - }); - } - - return commands.ToArray(); - } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/PowerToysExtensionCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/PowerToysExtensionCommandsProvider.cs index 4acac7260b..41d47bff18 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/PowerToysExtensionCommandsProvider.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/PowerToysExtensionCommandsProvider.cs @@ -2,10 +2,14 @@ // 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.Collections.Generic; +using System.Linq; using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.Extensions.Toolkit; +using PowerToysExtension.Commands; using PowerToysExtension.Helpers; +using PowerToysExtension.Pages; using PowerToysExtension.Properties; namespace PowerToysExtension; @@ -64,11 +68,54 @@ public partial class PowerToysExtensionCommandsProvider : CommandProvider } } - return null; + return TryGetFancyZonesCommandItem(id); } private void RaiseModuleItemsChanged() { RaiseItemsChanged(); } + + private static ICommandItem? TryGetFancyZonesCommandItem(string id) + { + if (!FancyZonesCommandIds.TryParseApplyLayoutCommandId(id, out var layoutId, out var monitorToken)) + { + return null; + } + + var layout = FancyZonesDataService.GetLayouts() + .FirstOrDefault(candidate => string.Equals(candidate.Id, layoutId, StringComparison.Ordinal)); + if (layout is null) + { + return null; + } + + var fallbackIcon = PowerToysResourcesHelper.IconFromSettingsIcon("FancyZones.png"); + if (string.IsNullOrWhiteSpace(monitorToken)) + { + FancyZonesDataService.TryGetMonitors(out var monitors, out _); + return new FancyZonesLayoutListItem(new ApplyFancyZonesLayoutCommand(layout, monitor: null), layout, fallbackIcon) + { + MoreCommands = FancyZonesContextHelper.BuildLayoutContext(layout, monitors), + }; + } + + if (!FancyZonesDataService.TryGetMonitors(out var availableMonitors, out _)) + { + return null; + } + + var monitor = availableMonitors + .FirstOrDefault(candidate => string.Equals(FancyZonesCommandIds.GetMonitorToken(candidate), monitorToken, StringComparison.Ordinal)); + + if (monitor.Equals(default(FancyZonesMonitorDescriptor))) + { + return null; + } + + return new FancyZonesLayoutListItem(new ApplyFancyZonesLayoutCommand(layout, monitor), layout, fallbackIcon) + { + Subtitle = FancyZonesContextHelper.FormatApplyToMonitorTitle(monitor), + }; + } }