mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-12 23:36:19 +01:00
Compare commits
5 Commits
stable
...
leilzh/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d72473cb4f | ||
|
|
c88fe1fa0e | ||
|
|
fd88fa18d4 | ||
|
|
a6b8cea7cd | ||
|
|
5f61057b38 |
@@ -66,5 +66,10 @@ namespace PowerToys.GPOWrapperProjection
|
||||
{
|
||||
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredWorkspacesEnabledValue();
|
||||
}
|
||||
|
||||
public static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue()
|
||||
{
|
||||
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredLightSwitchEnabledValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// 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.Globalization;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
@@ -41,15 +42,19 @@ internal sealed partial class FancyZonesMonitorListItem : ListItem
|
||||
public static Details BuildMonitorDetails(FancyZonesMonitorDescriptor monitor)
|
||||
{
|
||||
var currentVirtualDesktop = FancyZonesVirtualDesktop.GetCurrentVirtualDesktopIdString();
|
||||
|
||||
// Calculate physical resolution from logical pixels and DPI
|
||||
var scaleFactor = monitor.Data.Dpi > 0 ? monitor.Data.Dpi / 96.0 : 1.0;
|
||||
var physicalWidth = (int)Math.Round(monitor.Data.MonitorWidth * scaleFactor);
|
||||
var physicalHeight = (int)Math.Round(monitor.Data.MonitorHeight * scaleFactor);
|
||||
var resolution = $"{physicalWidth}\u00D7{physicalHeight}";
|
||||
|
||||
var tags = new List<IDetailsElement>
|
||||
{
|
||||
DetailTag(Resources.FancyZones_Monitor, monitor.Data.Monitor),
|
||||
DetailTag(Resources.FancyZones_Instance, monitor.Data.MonitorInstanceId),
|
||||
DetailTag(Resources.FancyZones_Serial, monitor.Data.MonitorSerialNumber),
|
||||
DetailTag(Resources.FancyZones_Number, monitor.Data.MonitorNumber.ToString(CultureInfo.InvariantCulture)),
|
||||
DetailTag(Resources.FancyZones_VirtualDesktop, currentVirtualDesktop),
|
||||
DetailTag(Resources.FancyZones_WorkArea, $"{monitor.Data.LeftCoordinate},{monitor.Data.TopCoordinate} {monitor.Data.WorkAreaWidth}\u00D7{monitor.Data.WorkAreaHeight}"),
|
||||
DetailTag(Resources.FancyZones_Resolution, $"{monitor.Data.MonitorWidth}\u00D7{monitor.Data.MonitorHeight}"),
|
||||
DetailTag(Resources.FancyZones_Resolution, resolution),
|
||||
DetailTag(Resources.FancyZones_DPI, monitor.Data.Dpi.ToString(CultureInfo.InvariantCulture)),
|
||||
};
|
||||
|
||||
|
||||
@@ -19,8 +19,12 @@ internal readonly record struct FancyZonesMonitorDescriptor(
|
||||
{
|
||||
get
|
||||
{
|
||||
var size = $"{Data.MonitorWidth}×{Data.MonitorHeight}";
|
||||
var scaling = Data.Dpi > 0 ? string.Format(CultureInfo.InvariantCulture, "{0}%", (int)Math.Round(Data.Dpi * 100 / 96.0)) : "n/a";
|
||||
// MonitorWidth/Height are logical (DPI-scaled) pixels, calculate physical resolution
|
||||
var scaleFactor = Data.Dpi > 0 ? Data.Dpi / 96.0 : 1.0;
|
||||
var physicalWidth = (int)Math.Round(Data.MonitorWidth * scaleFactor);
|
||||
var physicalHeight = (int)Math.Round(Data.MonitorHeight * scaleFactor);
|
||||
var size = $"{physicalWidth}×{physicalHeight}";
|
||||
var scaling = Data.Dpi > 0 ? string.Format(CultureInfo.InvariantCulture, "{0}%", (int)Math.Round(scaleFactor * 100)) : "n/a";
|
||||
return $"{size} \u2022 {scaling}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,12 +485,21 @@ internal static class FancyZonesThumbnailRenderer
|
||||
|
||||
private static List<NormalizedRect> GetFocusRects(int zoneCount)
|
||||
{
|
||||
// Focus layout parameters from FancyZonesEditor CanvasLayoutModel:
|
||||
// - DefaultOffset = 100px from top-left (normalized: ~0.05 for typical screen)
|
||||
// - OffsetShift = 50px per zone (normalized: ~0.025)
|
||||
// - ZoneSizeMultiplier = 0.4 (zones are 40% of screen)
|
||||
zoneCount = Math.Clamp(zoneCount, 1, 8);
|
||||
var rects = new List<NormalizedRect>(zoneCount);
|
||||
|
||||
const float defaultOffset = 0.05f; // ~100px on 1920px screen
|
||||
const float offsetShift = 0.025f; // ~50px on 1920px screen
|
||||
const float zoneSize = 0.4f; // 40% of screen
|
||||
|
||||
for (var i = 0; i < zoneCount; i++)
|
||||
{
|
||||
var offset = i * 0.06f;
|
||||
rects.Add(new NormalizedRect(0.1f + offset, 0.1f + offset, 0.8f, 0.8f));
|
||||
var offset = i * offsetShift;
|
||||
rects.Add(new NormalizedRect(defaultOffset + offset, defaultOffset + offset, zoneSize, zoneSize));
|
||||
}
|
||||
|
||||
return rects;
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.CommandLine.Invocation;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
using FancyZonesCLI.Utils;
|
||||
using FancyZonesEditorCommon.Data;
|
||||
using FancyZonesEditorCommon.Utils;
|
||||
|
||||
@@ -35,13 +36,19 @@ internal sealed partial class SetHotkeyCommand : FancyZonesBaseCommand
|
||||
{
|
||||
// FancyZones running guard is handled by FancyZonesBaseCommand.
|
||||
int key = context.ParseResult.GetValueForArgument(_key);
|
||||
string layout = context.ParseResult.GetValueForArgument(_layout);
|
||||
string layoutInput = context.ParseResult.GetValueForArgument(_layout);
|
||||
|
||||
if (key < 0 || key > 9)
|
||||
{
|
||||
throw new InvalidOperationException(Properties.Resources.set_hotkey_error_invalid_key);
|
||||
}
|
||||
|
||||
// Normalize GUID to Windows format with braces (supports input with or without braces)
|
||||
if (!GuidHelper.TryNormalizeGuid(layoutInput, out string layout))
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.set_hotkey_error_not_custom, layoutInput));
|
||||
}
|
||||
|
||||
// Editor only allows assigning hotkeys to existing custom layouts.
|
||||
var customLayouts = FancyZonesDataIO.ReadCustomLayouts();
|
||||
|
||||
@@ -60,7 +67,7 @@ internal sealed partial class SetHotkeyCommand : FancyZonesBaseCommand
|
||||
|
||||
if (!matchedLayout.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.set_hotkey_error_not_custom, layout));
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.set_hotkey_error_not_custom, layoutInput));
|
||||
}
|
||||
|
||||
string layoutName = matchedLayout.Value.Name;
|
||||
|
||||
@@ -140,9 +140,12 @@ internal sealed partial class SetLayoutCommand : FancyZonesBaseCommand
|
||||
return null;
|
||||
}
|
||||
|
||||
// Normalize GUID to Windows format with braces (supports input with or without braces)
|
||||
string normalizedLayout = GuidHelper.NormalizeGuid(layout) ?? layout;
|
||||
|
||||
foreach (var customLayout in customLayouts.CustomLayouts)
|
||||
{
|
||||
if (customLayout.Uuid.Equals(layout, StringComparison.OrdinalIgnoreCase))
|
||||
if (customLayout.Uuid.Equals(normalizedLayout, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return customLayout;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine;
|
||||
@@ -13,15 +14,15 @@ internal static class FancyZonesCliUsage
|
||||
public static void PrintUsage()
|
||||
{
|
||||
Console.OutputEncoding = System.Text.Encoding.UTF8;
|
||||
Console.WriteLine("FancyZones CLI - Command line interface for FancyZones");
|
||||
Console.WriteLine(Properties.Resources.usage_title);
|
||||
Console.WriteLine();
|
||||
|
||||
var cmd = FancyZonesCliCommandFactory.CreateRootCommand();
|
||||
|
||||
Console.WriteLine("Usage: FancyZonesCLI [command] [options]");
|
||||
Console.WriteLine(Properties.Resources.usage_syntax);
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Options:");
|
||||
Console.WriteLine(Properties.Resources.usage_options);
|
||||
foreach (var option in cmd.Options)
|
||||
{
|
||||
var aliases = string.Join(", ", option.Aliases);
|
||||
@@ -30,7 +31,7 @@ internal static class FancyZonesCliUsage
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Commands:");
|
||||
Console.WriteLine(Properties.Resources.usage_commands);
|
||||
foreach (var command in cmd.Subcommands)
|
||||
{
|
||||
if (command.IsHidden)
|
||||
@@ -51,7 +52,7 @@ internal static class FancyZonesCliUsage
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Examples:");
|
||||
Console.WriteLine(Properties.Resources.usage_examples);
|
||||
Console.WriteLine(" FancyZonesCLI --help");
|
||||
Console.WriteLine(" FancyZonesCLI --version");
|
||||
Console.WriteLine(" FancyZonesCLI get-monitors");
|
||||
@@ -59,4 +60,135 @@ internal static class FancyZonesCliUsage
|
||||
Console.WriteLine(" FancyZonesCLI set-layout <uuid> --monitor 1");
|
||||
Console.WriteLine(" FancyZonesCLI get-hotkeys");
|
||||
}
|
||||
|
||||
public static void PrintCommandUsage(string commandName)
|
||||
{
|
||||
Console.OutputEncoding = System.Text.Encoding.UTF8;
|
||||
|
||||
var rootCmd = FancyZonesCliCommandFactory.CreateRootCommand();
|
||||
|
||||
// Find matching subcommand by name or alias
|
||||
var subcommand = rootCmd.Subcommands.FirstOrDefault(c =>
|
||||
c.Aliases.Any(a => string.Equals(a, commandName, StringComparison.OrdinalIgnoreCase)));
|
||||
|
||||
if (subcommand == null)
|
||||
{
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.usage_unknown_command, commandName));
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(Properties.Resources.usage_run_help);
|
||||
return;
|
||||
}
|
||||
|
||||
// Command name and description
|
||||
Console.WriteLine($"{Properties.Resources.usage_command} {subcommand.Name}");
|
||||
if (!string.IsNullOrEmpty(subcommand.Description))
|
||||
{
|
||||
Console.WriteLine($" {subcommand.Description}");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
|
||||
// Usage line
|
||||
string argsLabel = string.Join(" ", subcommand.Arguments.Select(a => $"<{a.Name}>"));
|
||||
string optionsLabel = subcommand.Options.Any() ? " [options]" : string.Empty;
|
||||
Console.WriteLine($"Usage: FancyZonesCLI {subcommand.Name} {argsLabel}{optionsLabel}".TrimEnd());
|
||||
Console.WriteLine();
|
||||
|
||||
// Aliases
|
||||
var aliases = subcommand.Aliases.Where(a => !string.Equals(a, subcommand.Name, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
if (aliases.Count > 0)
|
||||
{
|
||||
Console.WriteLine($"{Properties.Resources.usage_aliases} {string.Join(", ", aliases)}");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
// Arguments
|
||||
if (subcommand.Arguments.Any())
|
||||
{
|
||||
Console.WriteLine(Properties.Resources.usage_arguments);
|
||||
foreach (var arg in subcommand.Arguments)
|
||||
{
|
||||
var argDescription = arg.Description ?? string.Empty;
|
||||
Console.WriteLine($" <{arg.Name}>{(arg.Arity.MinimumNumberOfValues == 0 ? $" {Properties.Resources.usage_optional}" : string.Empty),-20} {argDescription}");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
// Options
|
||||
if (subcommand.Options.Any())
|
||||
{
|
||||
Console.WriteLine(Properties.Resources.usage_options);
|
||||
foreach (var option in subcommand.Options)
|
||||
{
|
||||
var optAliases = string.Join(", ", option.Aliases);
|
||||
var optDescription = option.Description ?? string.Empty;
|
||||
Console.WriteLine($" {optAliases,-25} {optDescription}");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
// Command-specific examples
|
||||
PrintCommandExamples(subcommand.Name);
|
||||
}
|
||||
|
||||
private static void PrintCommandExamples(string commandName)
|
||||
{
|
||||
Console.WriteLine(Properties.Resources.usage_examples);
|
||||
|
||||
switch (commandName.ToLowerInvariant())
|
||||
{
|
||||
case "get-monitors":
|
||||
Console.WriteLine(" FancyZonesCLI get-monitors");
|
||||
Console.WriteLine(" FancyZonesCLI m");
|
||||
break;
|
||||
|
||||
case "get-layouts":
|
||||
Console.WriteLine(" FancyZonesCLI get-layouts");
|
||||
Console.WriteLine(" FancyZonesCLI ls");
|
||||
break;
|
||||
|
||||
case "get-active-layout":
|
||||
Console.WriteLine(" FancyZonesCLI get-active-layout");
|
||||
Console.WriteLine(" FancyZonesCLI active");
|
||||
break;
|
||||
|
||||
case "set-layout":
|
||||
Console.WriteLine(" FancyZonesCLI set-layout focus");
|
||||
Console.WriteLine(" FancyZonesCLI set-layout columns --monitor 1");
|
||||
Console.WriteLine(" FancyZonesCLI set-layout {uuid} --all");
|
||||
Console.WriteLine(" FancyZonesCLI s rows -m 2");
|
||||
break;
|
||||
|
||||
case "open-editor":
|
||||
Console.WriteLine(" FancyZonesCLI open-editor");
|
||||
Console.WriteLine(" FancyZonesCLI e");
|
||||
break;
|
||||
|
||||
case "open-settings":
|
||||
Console.WriteLine(" FancyZonesCLI open-settings");
|
||||
Console.WriteLine(" FancyZonesCLI settings");
|
||||
break;
|
||||
|
||||
case "get-hotkeys":
|
||||
Console.WriteLine(" FancyZonesCLI get-hotkeys");
|
||||
Console.WriteLine(" FancyZonesCLI hk");
|
||||
break;
|
||||
|
||||
case "set-hotkey":
|
||||
Console.WriteLine(" FancyZonesCLI set-hotkey 1 {layout-uuid}");
|
||||
Console.WriteLine(" FancyZonesCLI shk 2 0CEBCBA9-9C32-4395-B93E-DC77485AD6D0");
|
||||
break;
|
||||
|
||||
case "remove-hotkey":
|
||||
Console.WriteLine(" FancyZonesCLI remove-hotkey 1");
|
||||
Console.WriteLine(" FancyZonesCLI rhk 2");
|
||||
break;
|
||||
|
||||
default:
|
||||
Console.WriteLine($" FancyZonesCLI {commandName}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace FancyZonesCLI;
|
||||
|
||||
internal sealed class Program
|
||||
{
|
||||
private static readonly string[] HelpFlags = ["--help", "-h", "-?"];
|
||||
|
||||
private static async Task<int> Main(string[] args)
|
||||
{
|
||||
Logger.InitializeLogger();
|
||||
@@ -21,14 +23,17 @@ internal sealed class Program
|
||||
NativeMethods.InitializeWindowMessages();
|
||||
|
||||
// Intercept help requests early and print custom usage.
|
||||
if (args.Any(a => string.Equals(a, "--help", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(a, "-h", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(a, "-?", StringComparison.OrdinalIgnoreCase)))
|
||||
if (TryHandleHelpRequest(args))
|
||||
{
|
||||
FancyZonesCliUsage.PrintUsage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Detect PowerShell script block expansion (when {} is interpreted as script block)
|
||||
if (DetectPowerShellScriptBlockArgs(args))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
RootCommand rootCommand = FancyZonesCliCommandFactory.CreateRootCommand();
|
||||
int exitCode = await rootCommand.InvokeAsync(args);
|
||||
|
||||
@@ -43,4 +48,69 @@ internal sealed class Program
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles help requests for root command and subcommands.
|
||||
/// </summary>
|
||||
/// <returns>True if help was printed, false otherwise.</returns>
|
||||
private static bool TryHandleHelpRequest(string[] args)
|
||||
{
|
||||
bool hasHelpFlag = args.Any(a => HelpFlags.Any(h => string.Equals(a, h, StringComparison.OrdinalIgnoreCase)));
|
||||
if (!hasHelpFlag)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get non-help arguments to identify subcommand
|
||||
var nonHelpArgs = args.Where(a => !HelpFlags.Any(h => string.Equals(a, h, StringComparison.OrdinalIgnoreCase))).ToArray();
|
||||
|
||||
if (nonHelpArgs.Length == 0)
|
||||
{
|
||||
// Root help: fancyzones cli --help
|
||||
FancyZonesCliUsage.PrintUsage();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subcommand help: fancyzones cli <command> --help
|
||||
string subcommandName = nonHelpArgs[0];
|
||||
FancyZonesCliUsage.PrintCommandUsage(subcommandName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects when PowerShell interprets {GUID} as a script block and converts it to encoded command args.
|
||||
/// This happens when users forget to quote GUIDs with braces in PowerShell.
|
||||
/// </summary>
|
||||
/// <returns>True if PowerShell script block args were detected, false otherwise.</returns>
|
||||
private static bool DetectPowerShellScriptBlockArgs(string[] args)
|
||||
{
|
||||
// PowerShell converts {scriptblock} to: -encodedCommand <base64> -inputFormat xml -outputFormat text
|
||||
bool hasEncodedCommand = args.Any(a => string.Equals(a, "-encodedCommand", StringComparison.OrdinalIgnoreCase));
|
||||
bool hasInputFormat = args.Any(a => string.Equals(a, "-inputFormat", StringComparison.OrdinalIgnoreCase));
|
||||
bool hasOutputFormat = args.Any(a => string.Equals(a, "-outputFormat", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (hasEncodedCommand || (hasInputFormat && hasOutputFormat))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine(Properties.Resources.error_powershell_scriptblock_title);
|
||||
Console.ResetColor();
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(Properties.Resources.error_powershell_scriptblock_explanation);
|
||||
Console.WriteLine(Properties.Resources.error_powershell_scriptblock_hint);
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($" {Properties.Resources.error_powershell_scriptblock_option1}");
|
||||
Console.WriteLine($" {Properties.Resources.error_powershell_scriptblock_option1_example}");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($" {Properties.Resources.error_powershell_scriptblock_option2}");
|
||||
Console.WriteLine($" {Properties.Resources.error_powershell_scriptblock_option2_example}");
|
||||
Console.WriteLine();
|
||||
|
||||
Logger.LogWarning("PowerShell script block expansion detected - user needs to quote GUID or omit braces");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,5 +349,113 @@ namespace FancyZonesCLI.Properties {
|
||||
return ResourceManager.GetString("editor_params_timeout", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string error_powershell_scriptblock_title {
|
||||
get {
|
||||
return ResourceManager.GetString("error_powershell_scriptblock_title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string error_powershell_scriptblock_explanation {
|
||||
get {
|
||||
return ResourceManager.GetString("error_powershell_scriptblock_explanation", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string error_powershell_scriptblock_hint {
|
||||
get {
|
||||
return ResourceManager.GetString("error_powershell_scriptblock_hint", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string error_powershell_scriptblock_option1 {
|
||||
get {
|
||||
return ResourceManager.GetString("error_powershell_scriptblock_option1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string error_powershell_scriptblock_option1_example {
|
||||
get {
|
||||
return ResourceManager.GetString("error_powershell_scriptblock_option1_example", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string error_powershell_scriptblock_option2 {
|
||||
get {
|
||||
return ResourceManager.GetString("error_powershell_scriptblock_option2", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string error_powershell_scriptblock_option2_example {
|
||||
get {
|
||||
return ResourceManager.GetString("error_powershell_scriptblock_option2_example", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string usage_title {
|
||||
get {
|
||||
return ResourceManager.GetString("usage_title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string usage_syntax {
|
||||
get {
|
||||
return ResourceManager.GetString("usage_syntax", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string usage_options {
|
||||
get {
|
||||
return ResourceManager.GetString("usage_options", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string usage_commands {
|
||||
get {
|
||||
return ResourceManager.GetString("usage_commands", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string usage_examples {
|
||||
get {
|
||||
return ResourceManager.GetString("usage_examples", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string usage_arguments {
|
||||
get {
|
||||
return ResourceManager.GetString("usage_arguments", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string usage_aliases {
|
||||
get {
|
||||
return ResourceManager.GetString("usage_aliases", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string usage_command {
|
||||
get {
|
||||
return ResourceManager.GetString("usage_command", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string usage_optional {
|
||||
get {
|
||||
return ResourceManager.GetString("usage_optional", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string usage_unknown_command {
|
||||
get {
|
||||
return ResourceManager.GetString("usage_unknown_command", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string usage_run_help {
|
||||
get {
|
||||
return ResourceManager.GetString("usage_run_help", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,4 +230,62 @@ Tip: For templates, use the type name (e.g., 'focus', 'columns', 'rows', 'grid',
|
||||
<data name="editor_params_timeout" xml:space="preserve">
|
||||
<value>Could not get current monitor information (timed out after {0}ms waiting for '{1}').</value>
|
||||
</data>
|
||||
|
||||
<!-- PowerShell Script Block Detection -->
|
||||
<data name="error_powershell_scriptblock_title" xml:space="preserve">
|
||||
<value>Error: Invalid GUID format detected.</value>
|
||||
</data>
|
||||
<data name="error_powershell_scriptblock_explanation" xml:space="preserve">
|
||||
<value>PowerShell interprets curly braces {} as script blocks.</value>
|
||||
</data>
|
||||
<data name="error_powershell_scriptblock_hint" xml:space="preserve">
|
||||
<value>Please quote your GUID or use it without braces:</value>
|
||||
</data>
|
||||
<data name="error_powershell_scriptblock_option1" xml:space="preserve">
|
||||
<value>Option 1 - Quote the GUID:</value>
|
||||
</data>
|
||||
<data name="error_powershell_scriptblock_option1_example" xml:space="preserve">
|
||||
<value>FancyZonesCLI shk 1 '{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}'</value>
|
||||
</data>
|
||||
<data name="error_powershell_scriptblock_option2" xml:space="preserve">
|
||||
<value>Option 2 - Omit the braces (recommended):</value>
|
||||
</data>
|
||||
<data name="error_powershell_scriptblock_option2_example" xml:space="preserve">
|
||||
<value>FancyZonesCLI shk 1 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</value>
|
||||
</data>
|
||||
|
||||
<!-- CLI Usage -->
|
||||
<data name="usage_title" xml:space="preserve">
|
||||
<value>FancyZones CLI - Command line interface for FancyZones</value>
|
||||
</data>
|
||||
<data name="usage_syntax" xml:space="preserve">
|
||||
<value>Usage: FancyZonesCLI [command] [options]</value>
|
||||
</data>
|
||||
<data name="usage_options" xml:space="preserve">
|
||||
<value>Options:</value>
|
||||
</data>
|
||||
<data name="usage_commands" xml:space="preserve">
|
||||
<value>Commands:</value>
|
||||
</data>
|
||||
<data name="usage_examples" xml:space="preserve">
|
||||
<value>Examples:</value>
|
||||
</data>
|
||||
<data name="usage_arguments" xml:space="preserve">
|
||||
<value>Arguments:</value>
|
||||
</data>
|
||||
<data name="usage_aliases" xml:space="preserve">
|
||||
<value>Aliases:</value>
|
||||
</data>
|
||||
<data name="usage_command" xml:space="preserve">
|
||||
<value>Command:</value>
|
||||
</data>
|
||||
<data name="usage_optional" xml:space="preserve">
|
||||
<value>(optional)</value>
|
||||
</data>
|
||||
<data name="usage_unknown_command" xml:space="preserve">
|
||||
<value>Unknown command: {0}</value>
|
||||
</data>
|
||||
<data name="usage_run_help" xml:space="preserve">
|
||||
<value>Run 'FancyZonesCLI --help' to see available commands.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
52
src/modules/fancyzones/FancyZonesCLI/Utils/GuidHelper.cs
Normal file
52
src/modules/fancyzones/FancyZonesCLI/Utils/GuidHelper.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// 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;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace FancyZonesCLI.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for normalizing GUID strings to Windows format with braces.
|
||||
/// Supports input with or without braces: both "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
/// and "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" are accepted.
|
||||
/// </summary>
|
||||
internal static class GuidHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Normalizes a GUID string to Windows format with braces.
|
||||
/// Returns null if the input is not a valid GUID.
|
||||
/// </summary>
|
||||
/// <param name="input">GUID string with or without braces.</param>
|
||||
/// <returns>GUID in "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" format, or null if invalid.</returns>
|
||||
public static string? NormalizeGuid(string? input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Guid.TryParse(input, out Guid guid))
|
||||
{
|
||||
// "B" format includes braces: {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
|
||||
return guid.ToString("B").ToUpperInvariant();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to normalize a GUID string to Windows format with braces.
|
||||
/// </summary>
|
||||
/// <param name="input">GUID string with or without braces.</param>
|
||||
/// <param name="normalizedGuid">The normalized GUID string, or the original input if normalization fails.</param>
|
||||
/// <returns>True if the input was successfully normalized; otherwise, false.</returns>
|
||||
public static bool TryNormalizeGuid(string? input, [NotNullWhen(true)] out string? normalizedGuid)
|
||||
{
|
||||
normalizedGuid = NormalizeGuid(input);
|
||||
return normalizedGuid != null;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using static FancyZonesEditorCommon.Data.CustomLayouts;
|
||||
|
||||
@@ -23,8 +24,10 @@ namespace FancyZonesEditorCommon.Data
|
||||
{
|
||||
public struct CanvasZoneWrapper
|
||||
{
|
||||
[JsonPropertyName("X")]
|
||||
public int X { get; set; }
|
||||
|
||||
[JsonPropertyName("Y")]
|
||||
public int Y { get; set; }
|
||||
|
||||
public int Width { get; set; }
|
||||
|
||||
@@ -31,7 +31,7 @@ internal static class Program
|
||||
Console.InputEncoding = Encoding.Unicode;
|
||||
|
||||
// Initialize logger to file (same as other modules)
|
||||
CliLogger.Initialize("\\ImageResizer\\Logs");
|
||||
CliLogger.Initialize("\\Image Resizer\\CLI");
|
||||
CliLogger.Info($"ImageResizerCLI started with {args.Length} argument(s)");
|
||||
|
||||
try
|
||||
|
||||
@@ -216,27 +216,15 @@ namespace ImageResizer.Properties
|
||||
{
|
||||
if (e.PropertyName == nameof(Models.CustomSize))
|
||||
{
|
||||
var oldCustomSize = _customSize;
|
||||
_customSize = settings.CustomSize;
|
||||
|
||||
OnCollectionChanged(
|
||||
new NotifyCollectionChangedEventArgs(
|
||||
NotifyCollectionChangedAction.Replace,
|
||||
_customSize,
|
||||
oldCustomSize,
|
||||
_sizes.Count));
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
else if (e.PropertyName == nameof(Models.AiSize))
|
||||
{
|
||||
var oldAiSize = _aiSize;
|
||||
_aiSize = settings.AiSize;
|
||||
|
||||
OnCollectionChanged(
|
||||
new NotifyCollectionChangedEventArgs(
|
||||
NotifyCollectionChangedAction.Replace,
|
||||
_aiSize,
|
||||
oldAiSize,
|
||||
_sizes.Count + 1));
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
else if (e.PropertyName == nameof(Sizes))
|
||||
{
|
||||
|
||||
@@ -112,6 +112,13 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
return true;
|
||||
case ModuleType.LightSwitch:
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.LightSwitchToggleEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
AddFlyoutMenuItem(ModuleType.EnvironmentVariables);
|
||||
AddFlyoutMenuItem(ModuleType.FancyZones);
|
||||
AddFlyoutMenuItem(ModuleType.Hosts);
|
||||
AddFlyoutMenuItem(ModuleType.LightSwitch);
|
||||
AddFlyoutMenuItem(ModuleType.PowerLauncher);
|
||||
AddFlyoutMenuItem(ModuleType.PowerOCR);
|
||||
AddFlyoutMenuItem(ModuleType.RegistryPreview);
|
||||
@@ -120,6 +121,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
{
|
||||
ModuleType.ColorPicker => SettingsRepository<ColorPickerSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ActivationShortcut.ToString(),
|
||||
ModuleType.FancyZones => SettingsRepository<FancyZonesSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.FancyzonesEditorHotkey.Value.ToString(),
|
||||
ModuleType.LightSwitch => SettingsRepository<LightSwitchSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ToggleThemeHotkey.Value.ToString(),
|
||||
ModuleType.PowerLauncher => SettingsRepository<PowerLauncherSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.OpenPowerLauncher.ToString(),
|
||||
ModuleType.PowerOCR => SettingsRepository<PowerOcrSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ActivationShortcut.ToString(),
|
||||
ModuleType.Workspaces => SettingsRepository<WorkspacesSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.Hotkey.Value.ToString(),
|
||||
|
||||
@@ -11,7 +11,7 @@ using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
|
||||
namespace Settings.UI.Library
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
public class LightSwitchSettings : BasePTModuleSettings, ISettingsConfig, ICloneable, IHotkeyConfig
|
||||
{
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
[JsonSerializable(typeof(HostsSettings))]
|
||||
[JsonSerializable(typeof(ImageResizerSettings))]
|
||||
[JsonSerializable(typeof(KeyboardManagerSettings))]
|
||||
[JsonSerializable(typeof(SettingsUILibrary.LightSwitchSettings))]
|
||||
[JsonSerializable(typeof(LightSwitchSettings))]
|
||||
[JsonSerializable(typeof(MeasureToolSettings))]
|
||||
[JsonSerializable(typeof(MouseHighlighterSettings))]
|
||||
[JsonSerializable(typeof(MouseJumpSettings))]
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Microsoft.PowerToys.Settings.UI.SerializationContext;
|
||||
[JsonSerializable(typeof(FileLocksmithSettings))]
|
||||
[JsonSerializable(typeof(FindMyMouseSettings))]
|
||||
[JsonSerializable(typeof(IList<PowerToysReleaseInfo>))]
|
||||
[JsonSerializable(typeof(SettingsUILibrary.LightSwitchSettings))]
|
||||
[JsonSerializable(typeof(LightSwitchSettings))]
|
||||
[JsonSerializable(typeof(MeasureToolSettings))]
|
||||
[JsonSerializable(typeof(MouseHighlighterSettings))]
|
||||
[JsonSerializable(typeof(MouseJumpSettings))]
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
var darkSettings = this.moduleSettingsRepository.SettingsConfig;
|
||||
|
||||
// Pass them into the ViewModel
|
||||
this.ViewModel = new LightSwitchViewModel(darkSettings, this.sendConfigMsg);
|
||||
this.ViewModel = new LightSwitchViewModel(this.generalSettingsRepository, darkSettings, ShellPage.SendDefaultIPCMessage);
|
||||
this.ViewModel.PropertyChanged += ViewModel_PropertyChanged;
|
||||
|
||||
this.LoadSettings(this.generalSettingsRepository, this.moduleSettingsRepository);
|
||||
@@ -185,7 +185,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
// need to save the values
|
||||
this.ViewModel.Latitude = latitude.ToString(CultureInfo.InvariantCulture);
|
||||
this.ViewModel.Longitude = longitude.ToString(CultureInfo.InvariantCulture);
|
||||
this.ViewModel.SyncButtonInformation = $"{this.ViewModel.Latitude}<EFBFBD>, {this.ViewModel.Longitude}<EFBFBD>";
|
||||
this.ViewModel.SyncButtonInformation = $"{this.ViewModel.Latitude}°, {this.ViewModel.Longitude}°";
|
||||
|
||||
var result = SunCalc.CalculateSunriseSunset(latitude, longitude, DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);
|
||||
|
||||
@@ -293,18 +293,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
|
||||
private void UpdateEnabledState(bool recommendedState)
|
||||
{
|
||||
var enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredLightSwitchEnabledValue();
|
||||
|
||||
if (enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
|
||||
{
|
||||
// Get the enabled state from GPO.
|
||||
this.ViewModel.IsEnabledGpoConfigured = true;
|
||||
this.ViewModel.EnabledGPOConfiguration = enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ViewModel.IsEnabled = recommendedState;
|
||||
}
|
||||
ViewModel.RefreshEnabledState();
|
||||
}
|
||||
|
||||
private async void SyncLocationButton_Click(object sender, RoutedEventArgs e)
|
||||
|
||||
@@ -14,8 +14,10 @@ using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using Microsoft.PowerToys.Settings.UI.SerializationContext;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PowerToys.GPOWrapper;
|
||||
using Settings.UI.Library;
|
||||
using Settings.UI.Library.Helpers;
|
||||
|
||||
@@ -27,10 +29,16 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
private Func<string, int> SendConfigMSG { get; }
|
||||
|
||||
private GeneralSettings GeneralSettingsConfig { get; set; }
|
||||
|
||||
public ObservableCollection<SearchLocation> SearchLocations { get; } = new();
|
||||
|
||||
public LightSwitchViewModel(LightSwitchSettings initialSettings = null, Func<string, int> ipcMSGCallBackFunc = null)
|
||||
public LightSwitchViewModel(ISettingsRepository<GeneralSettings> settingsRepository, LightSwitchSettings initialSettings = null, Func<string, int> ipcMSGCallBackFunc = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(settingsRepository);
|
||||
GeneralSettingsConfig = settingsRepository.SettingsConfig;
|
||||
InitializeEnabledValue();
|
||||
|
||||
_moduleSettings = initialSettings ?? new LightSwitchSettings();
|
||||
SendConfigMSG = ipcMSGCallBackFunc;
|
||||
|
||||
@@ -58,6 +66,21 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
return hotkeysDict;
|
||||
}
|
||||
|
||||
private void InitializeEnabledValue()
|
||||
{
|
||||
_enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredLightSwitchEnabledValue();
|
||||
if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
|
||||
{
|
||||
// Get the enabled state from GPO.
|
||||
_enabledStateIsGPOConfigured = true;
|
||||
_isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
_isEnabled = GeneralSettingsConfig.Enabled.LightSwitch;
|
||||
}
|
||||
}
|
||||
|
||||
private void ForceLightNow()
|
||||
{
|
||||
Logger.LogInfo("Sending custom action: forceLight");
|
||||
@@ -93,33 +116,26 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_enabledStateIsGPOConfigured)
|
||||
{
|
||||
return _enabledGPOConfiguration;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _isEnabled;
|
||||
}
|
||||
}
|
||||
get => _isEnabled;
|
||||
|
||||
set
|
||||
{
|
||||
if (_isEnabled != value)
|
||||
if (_enabledStateIsGPOConfigured)
|
||||
{
|
||||
if (_enabledStateIsGPOConfigured)
|
||||
{
|
||||
// If it's GPO configured, shouldn't be able to change this state.
|
||||
return;
|
||||
}
|
||||
// If it's GPO configured, shouldn't be able to change this state.
|
||||
return;
|
||||
}
|
||||
|
||||
if (value != _isEnabled)
|
||||
{
|
||||
_isEnabled = value;
|
||||
|
||||
RefreshEnabledState();
|
||||
// Set the status in the general settings configuration
|
||||
GeneralSettingsConfig.Enabled.LightSwitch = value;
|
||||
OutGoingGeneralSettings snd = new OutGoingGeneralSettings(GeneralSettingsConfig);
|
||||
|
||||
NotifyPropertyChanged();
|
||||
SendConfigMSG(snd.ToString());
|
||||
OnPropertyChanged(nameof(IsEnabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,24 +143,16 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
public bool IsEnabledGpoConfigured
|
||||
{
|
||||
get => _enabledStateIsGPOConfigured;
|
||||
set
|
||||
{
|
||||
if (_enabledStateIsGPOConfigured != value)
|
||||
{
|
||||
_enabledStateIsGPOConfigured = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool EnabledGPOConfiguration
|
||||
public GpoRuleConfigured EnabledGPOConfiguration
|
||||
{
|
||||
get => _enabledGPOConfiguration;
|
||||
get => _enabledGpoRuleConfiguration;
|
||||
set
|
||||
{
|
||||
if (_enabledGPOConfiguration != value)
|
||||
if (_enabledGpoRuleConfiguration != value)
|
||||
{
|
||||
_enabledGPOConfiguration = value;
|
||||
_enabledGpoRuleConfiguration = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
@@ -575,7 +583,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
|
||||
private bool _enabledStateIsGPOConfigured;
|
||||
private bool _enabledGPOConfiguration;
|
||||
private GpoRuleConfigured _enabledGpoRuleConfiguration;
|
||||
private LightSwitchSettings _moduleSettings;
|
||||
private bool _isEnabled;
|
||||
private HotkeySettings _toggleThemeHotkey;
|
||||
|
||||
Reference in New Issue
Block a user