[DSC] Implement Microsoft.PowerToys.Configure DSCResource & winget support (#30918)

* [DSC] Microsoft.PowerToys.Configure module + winget configuration file support

* f: fix for an incorrect directory id reference

* f: update comment

* f: address review comments

* f: file locksmith bug fix

* f: add explorer preview switches in samples

* f: remove debug

* Sign DSC files

* f: implement docs/samples generator

* [ci]Sign FancyZonesEditorCommon.dll

* Sign DSC files in the Generated folder

* f: address review comments

* f: update usable options

* f: add autogenerated sample

* [Installer] Don't use same GUID for different components

* [Installer]Don't remove folders shared by other modules

* Allow configuring PTRun MaximumNumberOfResults

* Remove all settings DSC sample. Just random data

* Allow configuring Hosts Run as Administrator

* Revert "[Installer]Don't remove folders shared by other modules"

This reverts commit 6da3d6cfd5.

* Add all PTRun plugins and Global and keyboard to DSC sample

* Fix issues with context menu modules not disabling

* Fix default enabled values when setting with DSC

* Fix tests regarding default modules in Settings

* Fix merge error

* Restart PowerToys process if we stopped it

---------

Co-authored-by: Andrey Nekrasov <1828123+yuyoyuppe@users.noreply.github.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
This commit is contained in:
Andrey Nekrasov
2024-04-02 01:09:47 +02:00
committed by GitHub
parent 818d3e3035
commit f23fa3f592
81 changed files with 2608 additions and 265 deletions

View File

@@ -0,0 +1,16 @@
// 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 Settings.UI.Library.Attributes;
/// <summary>
/// Adding this attribute to a property makes it not configurable from the command line.
/// Typical use cases:
/// - Property represents internal module state.
/// - Property has a type that is unwieldy to type as a command line string.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class CmdConfigureIgnoreAttribute : Attribute;

View File

@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
@@ -36,6 +37,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public DateTimeOffset ExpirationDateTime { get; set; }
[JsonPropertyName("customTrayTimes")]
[CmdConfigureIgnoreAttribute]
public Dictionary<string, int> CustomTrayTimes { get; set; }
}

View File

@@ -7,7 +7,7 @@ using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class BoolProperty
public record BoolProperty : ICmdLineRepresentable
{
public BoolProperty()
{
@@ -22,9 +22,28 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonPropertyName("value")]
public bool Value { get; set; }
public static bool TryParseFromCmd(string cmd, out object result)
{
result = null;
if (!bool.TryParse(cmd, out bool value))
{
return false;
}
result = new BoolProperty { Value = value };
return true;
}
public override string ToString()
{
return JsonSerializer.Serialize(this);
}
public bool TryToCmdRepresentable(out string result)
{
result = Value.ToString();
return true;
}
}
}

View File

@@ -7,11 +7,13 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class ColorPickerProperties
{
[CmdConfigureIgnore]
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x43);
public ColorPickerProperties()
@@ -43,6 +45,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonPropertyName("changecursor")]
[JsonConverter(typeof(BoolPropertyJsonConverter))]
[CmdConfigureIgnoreAttribute]
public bool ChangeCursor { get; set; }
[JsonPropertyName("copiedcolorrepresentation")]
@@ -53,12 +56,15 @@ namespace Microsoft.PowerToys.Settings.UI.Library
// Property ColorHistory is not used, the color history is saved separately in the colorHistory.json file
[JsonPropertyName("colorhistory")]
[CmdConfigureIgnoreAttribute]
public List<string> ColorHistory { get; set; }
[JsonPropertyName("colorhistorylimit")]
[CmdConfigureIgnoreAttribute]
public int ColorHistoryLimit { get; set; }
[JsonPropertyName("visiblecolorformats")]
[CmdConfigureIgnoreAttribute]
public Dictionary<string, KeyValuePair<bool, string>> VisibleColorFormats { get; set; }
[JsonPropertyName("showcolorname")]

View File

@@ -15,6 +15,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
{
private Action notifyEnabledChangedAction;
// Default values for enabled modules should match their expected "enabled by default" values.
// Otherwise, a run of DSC on clean settings will not match the expected default result.
public EnabledModules()
{
}
@@ -55,7 +57,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
private bool fileExplorerPreview = true;
[JsonPropertyName("File Explorer Preview")]
public bool FileExplorerPreview
public bool PowerPreview
{
get => fileExplorerPreview;
set
@@ -116,7 +118,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
}
}
private bool keyboardManager = true;
private bool keyboardManager; // defaulting to off
[JsonPropertyName("Keyboard Manager")]
public bool KeyboardManager
@@ -183,7 +185,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
}
}
private bool awake;
private bool awake = true;
[JsonPropertyName("Awake")]
public bool Awake
@@ -199,7 +201,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
}
}
private bool mouseWithoutBorders = true;
private bool mouseWithoutBorders; // defaulting to off
[JsonPropertyName("MouseWithoutBorders")]
public bool MouseWithoutBorders
@@ -247,7 +249,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
}
}
private bool mouseJump = true;
private bool mouseJump; // defaulting to off
[JsonPropertyName("MouseJump")]
public bool MouseJump
@@ -279,7 +281,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
}
}
private bool mousePointerCrosshairs = true;
private bool mousePointerCrosshairs; // defaulting to off
[JsonPropertyName("MousePointerCrosshairs")]
public bool MousePointerCrosshairs
@@ -295,7 +297,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
}
}
private bool powerAccent;
private bool powerAccent; // defaulting to off
[JsonPropertyName("QuickAccent")]
public bool PowerAccent
@@ -311,10 +313,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
}
}
private bool powerOCR = true;
private bool powerOCR; // defaulting to off
[JsonPropertyName("TextExtractor")]
public bool PowerOCR
public bool PowerOcr
{
get => powerOCR;
set

View File

@@ -4,6 +4,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
@@ -110,6 +111,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public BoolProperty FancyzonesMakeDraggedWindowTransparent { get; set; }
[JsonPropertyName("fancyzones_allowPopupWindowSnap")]
[CmdConfigureIgnore]
public BoolProperty FancyzonesAllowPopupWindowSnap { get; set; }
[JsonPropertyName("fancyzones_allowChildWindowSnap")]

View File

@@ -3,11 +3,13 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class FindMyMouseProperties
{
[CmdConfigureIgnore]
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x46);
[JsonPropertyName("activation_method")]

View File

@@ -8,6 +8,7 @@ using System.Text.Json.Serialization;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
@@ -18,15 +19,18 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public bool Startup { get; set; }
// Gets or sets a value indicating whether the powertoy elevated.
[CmdConfigureIgnoreAttribute]
[JsonPropertyName("is_elevated")]
public bool IsElevated { get; set; }
// Gets or sets a value indicating whether powertoys should run elevated.
[JsonPropertyName("run_elevated")]
[CmdConfigureIgnoreAttribute]
public bool RunElevated { get; set; }
// Gets or sets a value indicating whether is admin.
[JsonPropertyName("is_admin")]
[CmdConfigureIgnoreAttribute]
public bool IsAdmin { get; set; }
// Gets or sets a value indicating whether is warnings of elevated apps enabled.
@@ -39,16 +43,20 @@ namespace Microsoft.PowerToys.Settings.UI.Library
// Gets or sets system theme name.
[JsonPropertyName("system_theme")]
[CmdConfigureIgnore]
public string SystemTheme { get; set; }
// Gets or sets powertoys version number.
[JsonPropertyName("powertoys_version")]
[CmdConfigureIgnore]
public string PowertoysVersion { get; set; }
[JsonPropertyName("action_name")]
[CmdConfigureIgnore]
public string CustomActionName { get; set; }
[JsonPropertyName("enabled")]
[CmdConfigureIgnore]
public EnabledModules Enabled { get; set; }
[JsonPropertyName("show_new_updates_toast_notification")]

View File

@@ -3,10 +3,11 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class GenericProperty<T>
public class GenericProperty<T> : ICmdLineRepresentable
{
[JsonPropertyName("value")]
public T Value { get; set; }
@@ -20,5 +21,25 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public GenericProperty()
{
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "Adding ICmdLineRepresentable support")]
public static bool TryParseFromCmd(string cmd, out object result)
{
result = null;
if (ICmdLineRepresentable.TryParseFromCmdFor(typeof(T), cmd, out var value))
{
result = new GenericProperty<T> { Value = (T)value };
return true;
}
return false;
}
public bool TryToCmdRepresentable(out string result)
{
result = Value.ToString();
return true;
}
}
}

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
using Settings.UI.Library.Attributes;
using Settings.UI.Library.Enumerations;
namespace Microsoft.PowerToys.Settings.UI.Library

View File

@@ -2,14 +2,16 @@
// 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 System.Text;
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class HotkeySettings
public record HotkeySettings : ICmdLineRepresentable
{
private const int VKTAB = 0x09;
@@ -39,11 +41,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
Code = code;
}
public HotkeySettings Clone()
{
return new HotkeySettings(Win, Ctrl, Alt, Shift, Code);
}
[JsonPropertyName("win")]
public bool Win { get; set; }
@@ -176,5 +173,72 @@ namespace Microsoft.PowerToys.Settings.UI.Library
return false;
}
public static bool TryParseFromCmd(string cmd, out object result)
{
bool win = false, ctrl = false, alt = false, shift = false;
int code = 0;
var parts = cmd.Split('+');
foreach (var part in parts)
{
switch (part.Trim().ToLower(CultureInfo.InvariantCulture))
{
case "win":
win = true;
break;
case "ctrl":
ctrl = true;
break;
case "alt":
alt = true;
break;
case "shift":
shift = true;
break;
default:
if (!TryParseKeyCode(part, out code))
{
result = null;
return false;
}
break;
}
}
result = new HotkeySettings(win, ctrl, alt, shift, code);
return true;
}
private static bool TryParseKeyCode(string key, out int keyCode)
{
// ASCII symbol
if (key.Length == 1 && char.IsLetterOrDigit(key[0]))
{
keyCode = char.ToUpper(key[0], CultureInfo.InvariantCulture);
return true;
}
// VK code
else if (key.Length == 4 && key.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
return int.TryParse(key.AsSpan(2), NumberStyles.HexNumber, null, out keyCode);
}
// Alias
else
{
keyCode = (int)Utilities.Helper.GetKeyValue(key);
return keyCode != 0;
}
}
public bool TryToCmdRepresentable(out string result)
{
result = ToString();
result = result.Replace(" ", null);
return true;
}
}
}

View File

@@ -6,6 +6,7 @@ using System;
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
@@ -69,6 +70,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public StringProperty ImageresizerFileName { get; set; }
[JsonPropertyName("imageresizer_sizes")]
[CmdConfigureIgnoreAttribute]
public ImageResizerSizes ImageresizerSizes { get; set; }
[JsonPropertyName("imageresizer_keepDateModified")]
@@ -78,6 +80,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public StringProperty ImageresizerFallbackEncoder { get; set; }
[JsonPropertyName("imageresizer_customSize")]
[CmdConfigureIgnoreAttribute]
public ImageResizerCustomSizeProperty ImageresizerCustomSize { get; set; }
public string ToJsonString()

View File

@@ -3,13 +3,14 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Library
{
// Represents the configuration property of the settings that store Integer type.
public class IntProperty
public record IntProperty : ICmdLineRepresentable
{
public IntProperty()
{
@@ -25,6 +26,19 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonPropertyName("value")]
public int Value { get; set; }
public static bool TryParseFromCmd(string cmd, out object result)
{
result = null;
if (!int.TryParse(cmd, out var value))
{
return false;
}
result = new IntProperty { Value = value };
return true;
}
// Returns a JSON version of the class settings configuration class.
public override string ToString()
{
@@ -40,5 +54,11 @@ namespace Microsoft.PowerToys.Settings.UI.Library
{
throw new NotImplementedException();
}
public bool TryToCmdRepresentable(out string result)
{
result = Value.ToString(CultureInfo.InvariantCulture);
return true;
}
}
}

View File

@@ -0,0 +1,126 @@
// 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.Globalization;
using System.Reflection;
namespace Microsoft.PowerToys.Settings.UI.Library;
/// <summary>
/// A helper interface to allow parsing property values from their command line representation.
/// </summary>
public interface ICmdLineRepresentable
{
public static abstract bool TryParseFromCmd(string cmd, out object result);
public abstract bool TryToCmdRepresentable(out string result);
public static sealed bool TryToCmdRepresentableFor(Type type, object value, out string result)
{
result = null;
if (!typeof(ICmdLineRepresentable).IsAssignableFrom(type))
{
throw new ArgumentException($"{type} doesn't implement {nameof(ICmdLineRepresentable)}");
}
var method = type.GetMethod(nameof(TryToCmdRepresentable));
var parameters = new object[] { result };
if ((bool)method.Invoke(value, parameters))
{
result = (string)parameters[0];
return true;
}
return false;
}
public static sealed bool TryParseFromCmdFor(Type type, string cmd, out object result)
{
result = null;
if (!typeof(ICmdLineRepresentable).IsAssignableFrom(type))
{
throw new ArgumentException($"{type} doesn't implement {nameof(ICmdLineRepresentable)}");
}
var method = type.GetMethod(nameof(TryParseFromCmd), BindingFlags.Static | BindingFlags.Public);
var parameters = new object[] { cmd, null };
if ((bool)method.Invoke(null, parameters))
{
result = parameters[1];
return true;
}
return false;
}
public static sealed object ParseFor(Type type, string cmdRepr)
{
if (type.IsEnum)
{
return Enum.Parse(type, cmdRepr);
}
else if (type.IsPrimitive)
{
if (type == typeof(bool))
{
return bool.Parse(cmdRepr.ToLowerInvariant());
}
else
{
// Converts numeric types like Uint32
return Convert.ChangeType(cmdRepr, type, CultureInfo.InvariantCulture);
}
}
else if (type.IsValueType && type == typeof(DateTimeOffset))
{
if (DateTimeOffset.TryParse(cmdRepr, out var structResult))
{
return structResult;
}
throw new ArgumentException($"Invalid DateTimeOffset format '{cmdRepr}'");
}
else if (type.IsClass)
{
if (type == typeof(string))
{
return cmdRepr;
}
else
{
TryParseFromCmdFor(type, cmdRepr, out var classResult);
return classResult;
}
}
throw new NotImplementedException($"Parsing type {type} is not supported yet");
}
public static string ToCmdRepr(Type type, object value)
{
if (type.IsEnum || type.IsPrimitive)
{
return value.ToString();
}
else if (type.IsValueType && type == typeof(DateTimeOffset))
{
return ((DateTimeOffset)value).ToString("o");
}
else if (type.IsClass)
{
if (type == typeof(string))
{
return (string)value;
}
else
{
TryToCmdRepresentableFor(type, value, out var result);
return result;
}
}
throw new NotImplementedException($"CmdRepr of {type} is not supported yet");
}
}

View File

@@ -2,11 +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.Globalization;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class KeyboardKeysProperty
public record KeyboardKeysProperty : ICmdLineRepresentable
{
public KeyboardKeysProperty()
{
@@ -20,5 +21,24 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonPropertyName("value")]
public HotkeySettings Value { get; set; }
public static bool TryParseFromCmd(string cmd, out object result)
{
if (!HotkeySettings.TryParseFromCmd(cmd, out var hotkey))
{
result = null;
return false;
}
else
{
result = new KeyboardKeysProperty { Value = (HotkeySettings)hotkey };
return true;
}
}
public bool TryToCmdRepresentable(out string result)
{
return Value.TryToCmdRepresentable(out result);
}
}
}

View File

@@ -5,16 +5,19 @@
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class KeyboardManagerProperties
{
[JsonPropertyName("activeConfiguration")]
[CmdConfigureIgnoreAttribute]
public GenericProperty<string> ActiveConfiguration { get; set; }
// List of all Keyboard Configurations.
[JsonPropertyName("keyboardConfigurations")]
[CmdConfigureIgnoreAttribute]
public GenericProperty<List<string>> KeyboardConfigurations { get; set; }
public KeyboardManagerProperties()

View File

@@ -4,12 +4,14 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Settings.UI.Library.Attributes;
using Settings.UI.Library.Enumerations;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class MeasureToolProperties
{
[CmdConfigureIgnore]
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x4D);
public MeasureToolProperties()
@@ -35,6 +37,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonConverter(typeof(BoolPropertyJsonConverter))]
public bool PerColorChannelEdgeDetection { get; set; }
[CmdConfigureIgnore]
public IntProperty UnitsOfMeasure { get; set; }
public IntProperty PixelTolerance { get; set; }

View File

@@ -3,11 +3,13 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class MouseHighlighterProperties
{
[CmdConfigureIgnore]
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x48);
[JsonPropertyName("activation_shortcut")]
@@ -20,6 +22,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public StringProperty RightButtonClickColor { get; set; }
[JsonPropertyName("highlight_opacity")]
[CmdConfigureIgnore]
public IntProperty HighlightOpacity { get; set; }
[JsonPropertyName("always_color")]

View File

@@ -3,11 +3,13 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class MouseJumpProperties
{
[CmdConfigureIgnore]
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x44);
[JsonPropertyName("activation_shortcut")]

View File

@@ -9,7 +9,7 @@ using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class MouseJumpThumbnailSize : INotifyPropertyChanged
public record MouseJumpThumbnailSize : INotifyPropertyChanged, ICmdLineRepresentable
{
private int _width;
private int _height;
@@ -64,5 +64,30 @@ namespace Microsoft.PowerToys.Settings.UI.Library
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public static bool TryParseFromCmd(string cmd, out object result)
{
result = null;
var parts = cmd.Split('x');
if (parts.Length != 2)
{
return false;
}
if (int.TryParse(parts[0], out int width) && int.TryParse(parts[1], out int height))
{
result = new MouseJumpThumbnailSize { Width = width, Height = height };
return true;
}
return false;
}
public bool TryToCmdRepresentable(out string result)
{
result = $"{Width}x{Height}";
return true;
}
}
}

View File

@@ -3,11 +3,13 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class MousePointerCrosshairsProperties
{
[CmdConfigureIgnore]
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, true, false, 0x50); // Win + Alt + P
[JsonPropertyName("activation_shortcut")]

View File

@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
@@ -23,16 +24,22 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public class MouseWithoutBordersProperties : ICloneable
{
[CmdConfigureIgnore]
public static HotkeySettings DefaultHotKeySwitch2AllPC => new HotkeySettings();
[CmdConfigureIgnore]
public static HotkeySettings DefaultHotKeyLockMachine => new HotkeySettings(true, true, true, false, 0x4C);
[CmdConfigureIgnore]
public static HotkeySettings DefaultHotKeyReconnect => new HotkeySettings(true, true, true, false, 0x52);
[CmdConfigureIgnore]
public static HotkeySettings DefaultHotKeyToggleEasyMouse => new HotkeySettings(true, true, true, false, 0x45);
[CmdConfigureIgnore]
public StringProperty SecurityKey { get; set; }
[CmdConfigureIgnore]
[JsonConverter(typeof(BoolPropertyJsonConverter))]
public bool UseService { get; set; }
@@ -72,42 +79,54 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonConverter(typeof(BoolPropertyJsonConverter))]
public bool ShowClipboardAndNetworkStatusMessages { get; set; }
[CmdConfigureIgnoreAttribute]
public List<string> MachineMatrixString { get; set; }
[CmdConfigureIgnoreAttribute]
public StringProperty MachinePool { get; set; }
[JsonConverter(typeof(BoolPropertyJsonConverter))]
[CmdConfigureIgnoreAttribute]
public bool MatrixOneRow { get; set; }
public IntProperty EasyMouse { get; set; }
[CmdConfigureIgnore]
public IntProperty MachineID { get; set; }
[CmdConfigureIgnoreAttribute]
public IntProperty LastX { get; set; }
[CmdConfigureIgnoreAttribute]
public IntProperty LastY { get; set; }
[CmdConfigureIgnoreAttribute]
public IntProperty PackageID { get; set; }
[JsonConverter(typeof(BoolPropertyJsonConverter))]
[CmdConfigureIgnoreAttribute]
public bool FirstRun { get; set; }
public IntProperty HotKeySwitchMachine { get; set; }
[ObsoleteAttribute("Use ToggleEasyMouseShortcut instead", false)]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[CmdConfigureIgnoreAttribute]
public IntProperty HotKeyToggleEasyMouse { get; set; }
[ObsoleteAttribute("Use LockMachineShortcut instead", false)]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[CmdConfigureIgnoreAttribute]
public IntProperty HotKeyLockMachine { get; set; }
[ObsoleteAttribute("Use ReconnectShortcut instead", false)]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[CmdConfigureIgnoreAttribute]
public IntProperty HotKeyReconnect { get; set; }
[ObsoleteAttribute("Use Switch2AllPCShortcut instead", false)]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[CmdConfigureIgnoreAttribute]
public IntProperty HotKeySwitch2AllPC { get; set; }
public HotkeySettings ToggleEasyMouseShortcut { get; set; }
@@ -118,6 +137,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public HotkeySettings Switch2AllPCShortcut { get; set; }
[CmdConfigureIgnoreAttribute]
public IntProperty TCPPort { get; set; }
[JsonConverter(typeof(BoolPropertyJsonConverter))]
@@ -126,8 +146,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public StringProperty Name2IP { get; set; }
[JsonConverter(typeof(BoolPropertyJsonConverter))]
[CmdConfigureIgnoreAttribute]
public bool FirstCtrlShiftS { get; set; }
[CmdConfigureIgnoreAttribute]
public StringProperty DeviceID { get; set; }
public MouseWithoutBordersProperties()

View File

@@ -3,11 +3,13 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class PastePlainProperties
{
[CmdConfigureIgnore]
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, true, true, false, 0x56); // Ctrl+Win+Alt+V
public PastePlainProperties()

View File

@@ -3,11 +3,13 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class PeekProperties
{
[CmdConfigureIgnore]
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(false, true, false, false, 0x20);
public PeekProperties()

View File

@@ -4,15 +4,18 @@
using System.Text.Json.Serialization;
using ManagedCommon;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class PowerLauncherProperties
{
[JsonPropertyName("search_result_preference")]
[CmdConfigureIgnoreAttribute]
public string SearchResultPreference { get; set; }
[JsonPropertyName("search_type_preference")]
[CmdConfigureIgnoreAttribute]
public string SearchTypePreference { get; set; }
[JsonPropertyName("maximum_number_of_results")]
@@ -22,18 +25,23 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public HotkeySettings OpenPowerLauncher { get; set; }
[JsonPropertyName("open_file_location")]
[CmdConfigureIgnoreAttribute]
public HotkeySettings OpenFileLocation { get; set; }
[JsonPropertyName("copy_path_location")]
[CmdConfigureIgnoreAttribute]
public HotkeySettings CopyPathLocation { get; set; }
[JsonPropertyName("open_console")]
[CmdConfigureIgnoreAttribute]
public HotkeySettings OpenConsole { get; set; }
[JsonPropertyName("override_win_r_key")]
[CmdConfigureIgnoreAttribute]
public bool OverrideWinkeyR { get; set; }
[JsonPropertyName("override_win_s_key")]
[CmdConfigureIgnoreAttribute]
public bool OverrideWinkeyS { get; set; }
[JsonPropertyName("ignore_hotkeys_in_fullscreen")]
@@ -49,6 +57,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public Theme Theme { get; set; }
[JsonPropertyName("show_plugins_overview")]
[CmdConfigureIgnore]
public int ShowPluginsOverview { get; set; }
[JsonPropertyName("title_fontsize")]
@@ -84,10 +93,13 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonPropertyName("generate_thumbnails_from_files")]
public bool GenerateThumbnailsFromFiles { get; set; }
[CmdConfigureIgnoreAttribute]
public HotkeySettings DefaultOpenPowerLauncher => new HotkeySettings(false, false, true, false, 32);
[CmdConfigureIgnoreAttribute]
public HotkeySettings DefaultOpenFileLocation => new HotkeySettings();
[CmdConfigureIgnoreAttribute]
public HotkeySettings DefaultCopyPathLocation => new HotkeySettings();
public PowerLauncherProperties()

View File

@@ -3,11 +3,13 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class PowerOcrProperties
{
[CmdConfigureIgnore]
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x54); // Win+Shift+T
public PowerOcrProperties()

View File

@@ -2,7 +2,9 @@
// 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.Text.Json.Serialization;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
@@ -16,12 +18,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library
ShowIcon = new BoolProperty();
ExtendedContextMenuOnly = new BoolProperty();
UseBoostLib = new BoolProperty();
Enabled = new BoolProperty();
}
[ObsoleteAttribute("Now controlled from the general settings", false)]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public BoolProperty Enabled { get; set; }
[JsonPropertyName("bool_persist_input")]
[CmdConfigureIgnoreAttribute]
public BoolProperty PersistState { get; set; }
[JsonPropertyName("bool_mru_enabled")]
@@ -31,6 +35,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public IntProperty MaxMRUSize { get; set; }
[JsonPropertyName("bool_show_icon_on_menu")]
[CmdConfigureIgnoreAttribute]
public BoolProperty ShowIcon { get; set; }
[JsonPropertyName("bool_show_extended_menu")]

View File

@@ -1,8 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\Version.props" />
<Import Project="..\..\Version.props" />
<PropertyGroup>
<TargetFrameworks>net8.0-windows</TargetFrameworks>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
<TargetFramework>net8.0-windows</TargetFramework>
<Version>$(Version).0</Version>
<Authors>Microsoft Corporation</Authors>
@@ -20,42 +22,42 @@
<RuntimeIdentifier>win-arm64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<None Include="backup_restore_settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<None Include="backup_restore_settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.IO.Abstractions" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.IO.Abstractions" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\common\interop\PowerToys.Interop.vcxproj" />
<ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\common\interop\PowerToys.Interop.vcxproj" />
<ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resources\Resources.Designer.cs">
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Update="Resources\Resources.Designer.cs">
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources\Resources.resx">
<SubType>Designer</SubType>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources\Resources.resx">
<SubType>Designer</SubType>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -3,11 +3,13 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class ShortcutGuideProperties
{
[CmdConfigureIgnore]
public HotkeySettings DefaultOpenShortcutGuide => new HotkeySettings(true, false, false, true, 0xBF);
public ShortcutGuideProperties()

View File

@@ -8,7 +8,7 @@ using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Library
{
// Represents the configuration property of the settings that store string type.
public class StringProperty
public record StringProperty : ICmdLineRepresentable
{
public StringProperty()
{
@@ -35,6 +35,18 @@ namespace Microsoft.PowerToys.Settings.UI.Library
return new StringProperty(v);
}
public static bool TryParseFromCmd(string cmd, out object result)
{
result = new StringProperty(cmd);
return true;
}
public bool TryToCmdRepresentable(out string result)
{
result = Value;
return true;
}
public static implicit operator StringProperty(string v)
{
return new StringProperty(v);

View File

@@ -0,0 +1,81 @@
// 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.Linq;
using System.Reflection;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Library;
public class CommandLineUtils
{
private static Type GetSettingsConfigType(string moduleName, Assembly settingsLibraryAssembly)
{
var settingsClassName = moduleName == "GeneralSettings" ? moduleName : moduleName + "Settings";
return settingsLibraryAssembly.GetType(typeof(CommandLineUtils).Namespace + "." + settingsClassName);
}
public static ISettingsConfig GetSettingsConfigFor(string moduleName, ISettingsUtils settingsUtils, Assembly settingsLibraryAssembly)
{
return GetSettingsConfigFor(GetSettingsConfigType(moduleName, settingsLibraryAssembly), settingsUtils);
}
/// Executes SettingsRepository<moduleSettingsType>.GetInstance(settingsUtils).SettingsConfig
public static ISettingsConfig GetSettingsConfigFor(Type moduleSettingsType, ISettingsUtils settingsUtils)
{
var genericSettingsRepositoryType = typeof(SettingsRepository<>);
var moduleSettingsRepositoryType = genericSettingsRepositoryType.MakeGenericType(moduleSettingsType);
// Note: GeneralSettings is only used here only to satisfy nameof constrains, i.e. the choice of this particular type doesn't have any special significance.
var getInstanceInfo = moduleSettingsRepositoryType.GetMethod(nameof(SettingsRepository<GeneralSettings>.GetInstance));
var settingsRepository = getInstanceInfo.Invoke(null, new object[] { settingsUtils });
var settingsConfigProperty = getInstanceInfo.ReturnType.GetProperty(nameof(SettingsRepository<GeneralSettings>.SettingsConfig));
return settingsConfigProperty.GetValue(settingsRepository) as ISettingsConfig;
}
public static Assembly GetSettingsAssembly()
{
return AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(a => a.GetName().Name == "PowerToys.Settings.UI.Lib");
}
public static object GetPropertyValue(string propertyName, ISettingsConfig settingsConfig)
{
var (settingInfo, properties) = LocateSetting(propertyName, settingsConfig);
return settingInfo.GetValue(properties);
}
public static object GetProperties(ISettingsConfig settingsConfig)
{
var settingsType = settingsConfig.GetType();
if (settingsType == typeof(GeneralSettings))
{
return settingsConfig;
}
var settingsConfigInfo = settingsType.GetProperty("Properties");
return settingsConfigInfo.GetValue(settingsConfig);
}
public static (PropertyInfo SettingInfo, object Properties) LocateSetting(string propertyName, ISettingsConfig settingsConfig)
{
var properties = GetProperties(settingsConfig);
var propertiesType = properties.GetType();
if (propertiesType == typeof(GeneralSettings) && propertyName.StartsWith("Enabled.", StringComparison.InvariantCulture))
{
var moduleNameToToggle = propertyName.Replace("Enabled.", string.Empty);
properties = propertiesType.GetProperty("Enabled").GetValue(properties);
propertiesType = properties.GetType();
propertyName = moduleNameToToggle;
}
return (propertiesType.GetProperty(propertyName), properties);
}
public static PropertyInfo GetSettingPropertyInfo(string propertyName, ISettingsConfig settingsConfig)
{
return LocateSetting(propertyName, settingsConfig).SettingInfo;
}
}

View File

@@ -0,0 +1,78 @@
// 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.Collections.Generic;
using System.Globalization;
using System.Text.Json;
using System.Xml;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library;
/// <summary>
/// This user flow allows DSC resources to use PowerToys.Settings executable to get settings values by querying them from command line using the following syntax:
/// PowerToys.Settings.exe get <path to a json file containing a list of modules and their corresponding properties>
///
/// Example: PowerToys.Settings.exe get %TEMP%\properties.json
/// `properties.json` file contents:
/// {
/// "AlwaysOnTop": ["FrameEnabled", "FrameAccentColor"],
/// "FancyZones": ["FancyzonesShiftDrag", "FancyzonesShowOnAllMonitors"]
/// }
///
/// Upon PowerToys.Settings.exe completion, it'll update `properties.json` file to contain something like this:
/// {
/// "AlwaysOnTop": {
/// "FrameEnabled": true,
/// "FrameAccentColor": "#0099cc"
/// },
/// "FancyZones": {
/// "FancyzonesShiftDrag": true,
/// "FancyzonesShowOnAllMonitors": false
/// }
/// }
/// </summary>
public sealed class GetSettingCommandLineCommand
{
private static JsonSerializerOptions _serializerOptions = new JsonSerializerOptions
{
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
};
public static string Execute(Dictionary<string, List<string>> settingNamesForModules)
{
var modulesSettings = new Dictionary<string, Dictionary<string, object>>();
var settingsAssembly = CommandLineUtils.GetSettingsAssembly();
var settingsUtils = new SettingsUtils();
var enabledModules = SettingsRepository<GeneralSettings>.GetInstance(settingsUtils).SettingsConfig.Enabled;
foreach (var (moduleName, settings) in settingNamesForModules)
{
var moduleSettings = new Dictionary<string, object>();
if (moduleName != nameof(GeneralSettings))
{
moduleSettings.Add("Enabled", typeof(EnabledModules).GetProperty(moduleName).GetValue(enabledModules));
}
var settingsConfig = CommandLineUtils.GetSettingsConfigFor(moduleName, settingsUtils, settingsAssembly);
foreach (var settingName in settings)
{
var value = CommandLineUtils.GetPropertyValue(settingName, settingsConfig);
if (value != null)
{
var cmdReprValue = ICmdLineRepresentable.ToCmdRepr(value.GetType(), value);
moduleSettings.Add(settingName, cmdReprValue);
}
}
modulesSettings.Add(moduleName, moduleSettings);
}
return JsonSerializer.Serialize(modulesSettings, _serializerOptions);
}
}

View File

@@ -103,6 +103,11 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Utilities
return LayoutMap.GetKeyName(key);
}
public static uint GetKeyValue(string key)
{
return LayoutMap.GetKeyValue(key);
}
public static string GetProductVersion()
{
return interop.CommonManaged.GetProductVersion();

View File

@@ -0,0 +1,109 @@
// 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.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library;
/// <summary>
/// This user flow allows DSC resources to use PowerToys.Settings executable to set custom settings values by suppling them from command line using the following syntax:
/// PowerToys.Settings.exe setAdditional <module struct name> <path to a json file containing the properties>
/// </summary>
public sealed class SetAdditionalSettingsCommandLineCommand
{
private static readonly string KeyPropertyName = "Name";
private static JsonSerializerOptions _serializerOptions = new JsonSerializerOptions
{
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
};
private struct AdditionalPropertyInfo
{
public string RootPropertyName;
public JsonValueKind RootObjectType;
}
private static readonly Dictionary<string, AdditionalPropertyInfo> SupportedAdditionalPropertiesInfoForModules = new Dictionary<string, AdditionalPropertyInfo> { { "PowerLauncher", new AdditionalPropertyInfo { RootPropertyName = "Plugins", RootObjectType = JsonValueKind.Array } } };
private static void ExecuteRootArray(JsonElement.ArrayEnumerator properties, IEnumerable<object> currentPropertyValuesArray)
{
// In case it's an array of object -> combine the existing values with the provided
var currentPropertyValueType = currentPropertyValuesArray.FirstOrDefault()?.GetType();
object matchedElement = null;
foreach (var arrayElement in properties)
{
var newElementPropertyValues = new Dictionary<string, object>();
foreach (var elementProperty in arrayElement.EnumerateObject())
{
var elementPropertyName = elementProperty.Name;
var elementPropertyType = currentPropertyValueType.GetProperty(elementPropertyName).PropertyType;
var elemePropertyValue = ICmdLineRepresentable.ParseFor(elementPropertyType, elementProperty.Value.ToString());
if (elementPropertyName == KeyPropertyName)
{
foreach (var currentElementValue in currentPropertyValuesArray)
{
var currentElementType = currentElementValue.GetType();
var keyPropertyNameInfo = currentElementType.GetProperty(KeyPropertyName);
var keyPropertyValue = keyPropertyNameInfo.GetValue(currentElementValue);
if (string.Equals(keyPropertyValue, elemePropertyValue))
{
matchedElement = currentElementValue;
break;
}
}
}
else
{
newElementPropertyValues.Add(elementPropertyName, elemePropertyValue);
}
}
if (matchedElement != null)
{
foreach (var overriddenProperty in newElementPropertyValues)
{
var propertyInfo = currentPropertyValueType.GetProperty(overriddenProperty.Key);
propertyInfo.SetValue(matchedElement, overriddenProperty.Value);
}
}
}
}
public static void Execute(string moduleName, JsonDocument settings, ISettingsUtils settingsUtils)
{
Assembly settingsLibraryAssembly = CommandLineUtils.GetSettingsAssembly();
var settingsConfig = CommandLineUtils.GetSettingsConfigFor(moduleName, settingsUtils, settingsLibraryAssembly);
var settingsConfigType = settingsConfig.GetType();
if (!SupportedAdditionalPropertiesInfoForModules.TryGetValue(moduleName, out var additionalPropertiesInfo))
{
return;
}
var propertyValueInfo = settingsConfigType.GetProperty(additionalPropertiesInfo.RootPropertyName);
var currentPropertyValue = propertyValueInfo.GetValue(settingsConfig);
// For now, only a certain data shapes are supported
switch (additionalPropertiesInfo.RootObjectType)
{
case JsonValueKind.Array:
ExecuteRootArray(settings.RootElement.EnumerateArray(), currentPropertyValue as IEnumerable<object>);
break;
default:
throw new NotImplementedException();
}
settingsUtils.SaveSettings(settingsConfig.ToJsonString(), settingsConfig.GetModuleName());
}
}

View File

@@ -0,0 +1,53 @@
// 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.Reflection;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library;
/// <summary>
/// This user flow allows DSC resources to use PowerToys.Settings executable to set settings values by suppling them from command line using the following syntax:
/// PowerToys.Settings.exe set <module struct name>.<field name> <field_value>
///
/// Example: PowerToys.Settings.exe set MeasureTool.MeasureCrossColor "#00FF00"
/// </summary>
public sealed class SetSettingCommandLineCommand
{
private static readonly char[] SettingNameSeparator = { '.' };
private static (string ModuleName, string PropertyName) ParseSettingName(string settingName)
{
var parts = settingName.Split(SettingNameSeparator, 2, StringSplitOptions.RemoveEmptyEntries);
return (parts[0], parts[1]);
}
public static void Execute(string settingName, string settingValue, ISettingsUtils settingsUtils)
{
Assembly settingsLibraryAssembly = CommandLineUtils.GetSettingsAssembly();
var (moduleName, propertyName) = ParseSettingName(settingName);
var settingsConfig = CommandLineUtils.GetSettingsConfigFor(moduleName, settingsUtils, settingsLibraryAssembly);
var propertyInfo = CommandLineUtils.GetSettingPropertyInfo(propertyName, settingsConfig);
if (propertyInfo == null)
{
throw new ArgumentException($"Property '{propertyName}' wasn't found");
}
if (propertyInfo.PropertyType.GetCustomAttribute<CmdConfigureIgnoreAttribute>() != null)
{
throw new ArgumentException($"Property '{propertyName}' is explicitly ignored");
}
// Execute settingsConfig.Properties.<propertyName> = settingValue
var propertyValue = ICmdLineRepresentable.ParseFor(propertyInfo.PropertyType, settingValue);
var (settingInfo, properties) = CommandLineUtils.LocateSetting(propertyName, settingsConfig);
settingInfo.SetValue(properties, propertyValue);
settingsUtils.SaveSettings(settingsConfig.ToJsonString(), settingsConfig.GetModuleName());
}
}

View File

@@ -4,11 +4,13 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Settings.UI.Library.Attributes;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class VideoConferenceConfigProperties
{
[CmdConfigureIgnoreAttribute]
public HotkeySettings DefaultMuteCameraAndMicrophoneHotkey => new HotkeySettings()
{
Win = true,
@@ -19,6 +21,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
Code = 81,
};
[CmdConfigureIgnoreAttribute]
public HotkeySettings DefaultMuteMicrophoneHotkey => new HotkeySettings()
{
Win = true,
@@ -29,6 +32,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
Code = 65,
};
[CmdConfigureIgnoreAttribute]
public HotkeySettings DefaultPushToTalkMicrophoneHotkey => new HotkeySettings()
{
Win = true,
@@ -39,6 +43,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
Code = 73,
};
[CmdConfigureIgnoreAttribute]
public HotkeySettings DefaultMuteCameraHotkey => new HotkeySettings()
{
Win = true,

View File

@@ -9,10 +9,12 @@ namespace Microsoft.PowerToys.Settings.UI.Library
{
public class VideoConferenceSettings : BasePTModuleSettings, ISettingsConfig
{
public const string ModuleName = "Video Conference";
public VideoConferenceSettings()
{
Version = "1";
Name = "Video Conference";
Name = ModuleName;
Properties = new VideoConferenceConfigProperties();
}

View File

@@ -0,0 +1,75 @@
// 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 Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Settings.UI.UnitTests.Settings;
[TestClass]
public class ICmdReprParsableTests
{
[TestMethod]
public void KeyboardKeysPropertyParsing()
{
{
Assert.IsTrue(KeyboardKeysProperty.TryParseFromCmd("win+ctrl+Alt+sHifT+Q", out var hotkey));
Assert.AreEqual(new KeyboardKeysProperty { Value = new HotkeySettings(true, true, true, true, 0x51) }, hotkey);
}
{
Assert.IsTrue(KeyboardKeysProperty.TryParseFromCmd("CTRL+z", out var hotkey));
Assert.AreEqual(new KeyboardKeysProperty { Value = new HotkeySettings(false, true, false, false, 0x5A) }, hotkey);
}
{
Assert.IsTrue(KeyboardKeysProperty.TryParseFromCmd("shift+ALT+0x59", out var hotkey));
Assert.AreEqual(new KeyboardKeysProperty { Value = new HotkeySettings(false, false, true, true, 0x59) }, hotkey);
}
{
Assert.IsTrue(KeyboardKeysProperty.TryParseFromCmd("alt+Space", out var hotkey));
Assert.AreEqual(new KeyboardKeysProperty { Value = new HotkeySettings(false, false, true, false, 0x20) }, hotkey);
}
}
[TestMethod]
public void BoolPropertyParsing()
{
{
Assert.IsTrue(BoolProperty.TryParseFromCmd("True", out var result));
Assert.AreEqual(new BoolProperty(true), result);
}
{
Assert.IsTrue(BoolProperty.TryParseFromCmd("false", out var result));
Assert.AreEqual(new BoolProperty(false), result);
}
}
[TestMethod]
public void IntPropertyParsing()
{
{
Assert.IsTrue(IntProperty.TryParseFromCmd("123", out var result));
Assert.AreEqual(new IntProperty(123), result);
}
{
Assert.IsTrue(IntProperty.TryParseFromCmd("1500", out var result));
Assert.AreEqual(new IntProperty(1500), result);
Assert.AreNotEqual(new IntProperty(15), result);
}
}
[TestMethod]
public void MouseJumpThumbnailSizeParsing()
{
{
Assert.IsTrue(MouseJumpThumbnailSize.TryParseFromCmd("1920x1080", out var result));
Assert.AreEqual(new MouseJumpThumbnailSize { Width = 1920, Height = 1080 }, result);
}
}
}

View File

@@ -0,0 +1,63 @@
// 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.IO.Abstractions.TestingHelpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using static Microsoft.PowerToys.Settings.UI.Library.SetSettingCommandLineCommand;
namespace Settings.UI.UnitTests.Cmd;
[TestClass]
public class SetSettingCommandTests
{
private SettingsUtils settingsUtils;
[TestInitialize]
public void Setup()
{
settingsUtils = new SettingsUtils(new MockFileSystem());
}
private void SetSetting(Type moduleSettingsType, string settingName, string newValueStr)
{
var settings = CommandLineUtils.GetSettingsConfigFor(moduleSettingsType, settingsUtils);
var defaultValue = CommandLineUtils.GetPropertyValue(settingName, settings);
var qualifiedName = moduleSettingsType.Name.Replace("Settings", string.Empty) + "." + settingName;
var type = CommandLineUtils.GetSettingPropertyInfo(settingName, settings).PropertyType;
var newValue = ICmdLineRepresentable.ParseFor(type, newValueStr);
Execute(qualifiedName, newValueStr, settingsUtils);
Assert.AreNotEqual(defaultValue, newValue);
Assert.AreEqual(newValue, CommandLineUtils.GetPropertyValue(settingName, settings));
}
// Each setting has a different type.
[TestMethod]
[DataRow(typeof(PowerRenameSettings), nameof(PowerRenameProperties.MaxMRUSize), "123")]
[DataRow(typeof(FancyZonesSettings), nameof(FZConfigProperties.FancyzonesBorderColor), "#00FF00")]
[DataRow(typeof(MeasureToolSettings), nameof(MeasureToolProperties.ActivationShortcut), "Ctrl+Alt+Delete")]
[DataRow(typeof(AlwaysOnTopSettings), nameof(AlwaysOnTopProperties.SoundEnabled), "False")]
[DataRow(typeof(PowerAccentSettings), nameof(PowerAccentProperties.ShowUnicodeDescription), "true")]
[DataRow(typeof(AwakeSettings), nameof(AwakeProperties.Mode), "EXPIRABLE")]
[DataRow(typeof(AwakeSettings), nameof(AwakeProperties.ExpirationDateTime), "March 31, 2020 15:00 +00:00")]
[DataRow(typeof(PowerLauncherSettings), nameof(PowerLauncherProperties.MaximumNumberOfResults), "322")]
[DataRow(typeof(ColorPickerSettings), nameof(ColorPickerProperties.CopiedColorRepresentation), "RGB")]
public void SetModuleSetting(Type moduleSettingsType, string settingName, string newValueStr)
{
SetSetting(moduleSettingsType, settingName, newValueStr);
}
[DataRow(typeof(GeneralSettings), "Enabled.MouseWithoutBorders", "true")]
[DataRow(typeof(GeneralSettings), nameof(GeneralSettings.AutoDownloadUpdates), "true")]
[TestMethod]
public void SetGeneralSetting(Type moduleSettingsType, string settingName, string newValueStr)
{
SetSetting(moduleSettingsType, settingName, newValueStr);
}
}

View File

@@ -237,10 +237,9 @@ namespace ViewModelTests
// Assert
Assert.IsTrue(modules.FancyZones);
Assert.IsTrue(modules.ImageResizer);
Assert.IsTrue(modules.FileExplorerPreview);
Assert.IsTrue(modules.PowerPreview);
Assert.IsTrue(modules.ShortcutGuide);
Assert.IsTrue(modules.PowerRename);
Assert.IsTrue(modules.KeyboardManager);
Assert.IsTrue(modules.PowerLauncher);
Assert.IsTrue(modules.ColorPicker);
}

View File

@@ -67,7 +67,7 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers
case ModuleType.RegistryPreview: return generalSettingsConfig.Enabled.RegistryPreview;
case ModuleType.MeasureTool: return generalSettingsConfig.Enabled.MeasureTool;
case ModuleType.ShortcutGuide: return generalSettingsConfig.Enabled.ShortcutGuide;
case ModuleType.PowerOCR: return generalSettingsConfig.Enabled.PowerOCR;
case ModuleType.PowerOCR: return generalSettingsConfig.Enabled.PowerOcr;
default: return false;
}
}
@@ -99,7 +99,7 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers
case ModuleType.RegistryPreview: generalSettingsConfig.Enabled.RegistryPreview = isEnabled; break;
case ModuleType.MeasureTool: generalSettingsConfig.Enabled.MeasureTool = isEnabled; break;
case ModuleType.ShortcutGuide: generalSettingsConfig.Enabled.ShortcutGuide = isEnabled; break;
case ModuleType.PowerOCR: generalSettingsConfig.Enabled.PowerOCR = isEnabled; break;
case ModuleType.PowerOCR: generalSettingsConfig.Enabled.PowerOcr = isEnabled; break;
}
}

View File

@@ -3,9 +3,15 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http.Json;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Common.UI;
using interop;
@@ -42,8 +48,11 @@ namespace Microsoft.PowerToys.Settings.UI
ContainsFlyoutPosition,
}
// Quantity of arguments
private const int RequiredArgumentsQty = 12;
private const int RequiredArgumentsSetSettingQty = 4;
private const int RequiredArgumentsSetAdditionalSettingsQty = 4;
private const int RequiredArgumentsGetSettingQty = 3;
private const int RequiredArgumentsLaunchedFromRunnerQty = 12;
// Create an instance of the IPC wrapper.
private static TwoWayPipeMessageIPCManaged ipcmanager;
@@ -100,6 +109,171 @@ namespace Microsoft.PowerToys.Settings.UI
}
}
private void OnLaunchedToSetSetting(string[] cmdArgs)
{
var settingName = cmdArgs[2];
var settingValue = cmdArgs[3];
try
{
SetSettingCommandLineCommand.Execute(settingName, settingValue, new SettingsUtils());
}
catch (Exception ex)
{
Logger.LogError($"SetSettingCommandLineCommand exception: '{settingName}' setting couldn't be set to {settingValue}", ex);
}
Exit();
}
private void OnLaunchedToSetAdditionalSetting(string[] cmdArgs)
{
var moduleName = cmdArgs[2];
var ipcFileName = cmdArgs[3];
try
{
using (var settings = JsonDocument.Parse(File.ReadAllText(ipcFileName)))
{
SetAdditionalSettingsCommandLineCommand.Execute(moduleName, settings, new SettingsUtils());
}
}
catch (Exception ex)
{
Logger.LogError($"SetAdditionalSettingsCommandLineCommand exception: couldn't set additional settings for '{moduleName}'", ex);
}
Exit();
}
private void OnLaunchedToGetSetting(string[] cmdArgs)
{
var ipcFileName = cmdArgs[2];
try
{
var requestedSettings = JsonSerializer.Deserialize<Dictionary<string, List<string>>>(File.ReadAllText(ipcFileName));
File.WriteAllText(ipcFileName, GetSettingCommandLineCommand.Execute(requestedSettings));
}
catch (Exception ex)
{
Logger.LogError($"GetSettingCommandLineCommand exception", ex);
}
Exit();
}
private void OnLaunchedFromRunner(string[] cmdArgs)
{
// Skip the first argument which is prepended when launched by explorer
if (cmdArgs[0].EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase) && cmdArgs[1].EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase) && (cmdArgs.Length >= RequiredArgumentsLaunchedFromRunnerQty + 1))
{
cmdArgs = cmdArgs.Skip(1).ToArray();
}
_ = int.TryParse(cmdArgs[(int)Arguments.PTPid], out int powerToysPID);
PowerToysPID = powerToysPID;
IsElevated = cmdArgs[(int)Arguments.ElevatedStatus] == "true";
IsUserAnAdmin = cmdArgs[(int)Arguments.IsUserAdmin] == "true";
ShowOobe = cmdArgs[(int)Arguments.ShowOobeWindow] == "true";
ShowScoobe = cmdArgs[(int)Arguments.ShowScoobeWindow] == "true";
ShowFlyout = cmdArgs[(int)Arguments.ShowFlyout] == "true";
bool containsSettingsWindow = cmdArgs[(int)Arguments.ContainsSettingsWindow] == "true";
bool containsFlyoutPosition = cmdArgs[(int)Arguments.ContainsFlyoutPosition] == "true";
// To keep track of variable arguments
int currentArgumentIndex = RequiredArgumentsLaunchedFromRunnerQty;
if (containsSettingsWindow)
{
// Open specific window
StartupPage = GetPage(cmdArgs[currentArgumentIndex]);
currentArgumentIndex++;
}
int flyout_x = 0;
int flyout_y = 0;
if (containsFlyoutPosition)
{
// get the flyout position arguments
_ = int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_x);
_ = int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_y);
}
RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () =>
{
Environment.Exit(0);
});
ipcmanager = new TwoWayPipeMessageIPCManaged(cmdArgs[(int)Arguments.SettingsPipeName], cmdArgs[(int)Arguments.PTPipeName], (string message) =>
{
if (IPCMessageReceivedCallback != null && message.Length > 0)
{
IPCMessageReceivedCallback(message);
}
});
ipcmanager.Start();
if (!ShowOobe && !ShowScoobe && !ShowFlyout)
{
settingsWindow = new MainWindow();
settingsWindow.Activate();
settingsWindow.ExtendsContentIntoTitleBar = true;
settingsWindow.NavigateToSection(StartupPage);
// https://github.com/microsoft/microsoft-ui-xaml/issues/7595 - Activate doesn't bring window to the foreground
// Need to call SetForegroundWindow to actually gain focus.
WindowHelpers.BringToForeground(settingsWindow.GetWindowHandle());
}
else
{
// Create the Settings window hidden so that it's fully initialized and
// it will be ready to receive the notification if the user opens
// the Settings from the tray icon.
settingsWindow = new MainWindow(true);
if (ShowOobe)
{
PowerToysTelemetry.Log.WriteEvent(new OobeStartedEvent());
OobeWindow oobeWindow = new OobeWindow(OOBE.Enums.PowerToysModules.Overview);
oobeWindow.Activate();
oobeWindow.ExtendsContentIntoTitleBar = true;
SetOobeWindow(oobeWindow);
}
else if (ShowScoobe)
{
PowerToysTelemetry.Log.WriteEvent(new ScoobeStartedEvent());
OobeWindow scoobeWindow = new OobeWindow(OOBE.Enums.PowerToysModules.WhatsNew);
scoobeWindow.Activate();
scoobeWindow.ExtendsContentIntoTitleBar = true;
SetOobeWindow(scoobeWindow);
}
else if (ShowFlyout)
{
POINT? p = null;
if (containsFlyoutPosition)
{
p = new POINT(flyout_x, flyout_y);
}
ShellPage.OpenFlyoutCallback(p);
}
}
if (SelectedTheme() == ElementTheme.Default)
{
try
{
themeListener = new ThemeListener();
themeListener.ThemeChanged += (_) => HandleThemeChange();
}
catch (Exception ex)
{
Logger.LogError($"HandleThemeChange exception. Please install .NET 4.", ex);
}
}
}
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
@@ -109,117 +283,21 @@ namespace Microsoft.PowerToys.Settings.UI
{
var cmdArgs = Environment.GetCommandLineArgs();
if (cmdArgs != null && cmdArgs.Length >= RequiredArgumentsQty)
if (cmdArgs?.Length >= RequiredArgumentsLaunchedFromRunnerQty)
{
// Skip the first argument which is prepended when launched by explorer
if (cmdArgs[0].EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase) && cmdArgs[1].EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase) && (cmdArgs.Length >= RequiredArgumentsQty + 1))
{
cmdArgs = cmdArgs.Skip(1).ToArray();
}
_ = int.TryParse(cmdArgs[(int)Arguments.PTPid], out int powerToysPID);
PowerToysPID = powerToysPID;
IsElevated = cmdArgs[(int)Arguments.ElevatedStatus] == "true";
IsUserAnAdmin = cmdArgs[(int)Arguments.IsUserAdmin] == "true";
ShowOobe = cmdArgs[(int)Arguments.ShowOobeWindow] == "true";
ShowScoobe = cmdArgs[(int)Arguments.ShowScoobeWindow] == "true";
ShowFlyout = cmdArgs[(int)Arguments.ShowFlyout] == "true";
bool containsSettingsWindow = cmdArgs[(int)Arguments.ContainsSettingsWindow] == "true";
bool containsFlyoutPosition = cmdArgs[(int)Arguments.ContainsFlyoutPosition] == "true";
// To keep track of variable arguments
int currentArgumentIndex = RequiredArgumentsQty;
if (containsSettingsWindow)
{
// Open specific window
StartupPage = GetPage(cmdArgs[currentArgumentIndex]);
currentArgumentIndex++;
}
int flyout_x = 0;
int flyout_y = 0;
if (containsFlyoutPosition)
{
// get the flyout position arguments
_ = int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_x);
_ = int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_y);
}
RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () =>
{
Environment.Exit(0);
});
ipcmanager = new TwoWayPipeMessageIPCManaged(cmdArgs[(int)Arguments.SettingsPipeName], cmdArgs[(int)Arguments.PTPipeName], (string message) =>
{
if (IPCMessageReceivedCallback != null && message.Length > 0)
{
IPCMessageReceivedCallback(message);
}
});
ipcmanager.Start();
if (!ShowOobe && !ShowScoobe && !ShowFlyout)
{
settingsWindow = new MainWindow();
settingsWindow.Activate();
settingsWindow.ExtendsContentIntoTitleBar = true;
settingsWindow.NavigateToSection(StartupPage);
// https://github.com/microsoft/microsoft-ui-xaml/issues/7595 - Activate doesn't bring window to the foreground
// Need to call SetForegroundWindow to actually gain focus.
WindowHelpers.BringToForeground(settingsWindow.GetWindowHandle());
}
else
{
// Create the Settings window hidden so that it's fully initialized and
// it will be ready to receive the notification if the user opens
// the Settings from the tray icon.
settingsWindow = new MainWindow(true);
if (ShowOobe)
{
PowerToysTelemetry.Log.WriteEvent(new OobeStartedEvent());
OobeWindow oobeWindow = new OobeWindow(OOBE.Enums.PowerToysModules.Overview);
oobeWindow.Activate();
oobeWindow.ExtendsContentIntoTitleBar = true;
SetOobeWindow(oobeWindow);
}
else if (ShowScoobe)
{
PowerToysTelemetry.Log.WriteEvent(new ScoobeStartedEvent());
OobeWindow scoobeWindow = new OobeWindow(OOBE.Enums.PowerToysModules.WhatsNew);
scoobeWindow.Activate();
scoobeWindow.ExtendsContentIntoTitleBar = true;
SetOobeWindow(scoobeWindow);
}
else if (ShowFlyout)
{
POINT? p = null;
if (containsFlyoutPosition)
{
p = new POINT(flyout_x, flyout_y);
}
ShellPage.OpenFlyoutCallback(p);
}
}
if (SelectedTheme() == ElementTheme.Default)
{
try
{
themeListener = new ThemeListener();
themeListener.ThemeChanged += (_) => HandleThemeChange();
}
catch (Exception ex)
{
Logger.LogError($"HandleThemeChange exception. Please install .NET 4.", ex);
}
}
OnLaunchedFromRunner(cmdArgs);
}
else if (cmdArgs?.Length == RequiredArgumentsSetSettingQty && cmdArgs[1] == "set")
{
OnLaunchedToSetSetting(cmdArgs);
}
else if (cmdArgs?.Length == RequiredArgumentsSetAdditionalSettingsQty && cmdArgs[1] == "setAdditional")
{
OnLaunchedToSetAdditionalSetting(cmdArgs);
}
else if (cmdArgs?.Length == RequiredArgumentsGetSettingQty && cmdArgs[1] == "get")
{
OnLaunchedToGetSetting(cmdArgs);
}
else
{
@@ -417,7 +495,7 @@ namespace Microsoft.PowerToys.Settings.UI
case "QuickAccent": return typeof(PowerAccentPage);
case "FileExplorer": return typeof(PowerPreviewPage);
case "ShortcutGuide": return typeof(ShortcutGuidePage);
case "PowerOCR": return typeof(PowerOcrPage);
case "PowerOcr": return typeof(PowerOcrPage);
case "VideoConference": return typeof(VideoConferencePage);
case "MeasureTool": return typeof(MeasureToolPage);
case "Hosts": return typeof(HostsPage);

View File

@@ -321,7 +321,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
// Tab and Shift+Tab are accessible keys and should not be displayed in the hotkey control.
if (internalSettings.Code > 0 && !internalSettings.IsAccessibleShortcut())
{
lastValidSettings = internalSettings.Clone();
lastValidSettings = internalSettings with { };
if (!ComboIsValid(lastValidSettings))
{
@@ -436,7 +436,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
{
if (ComboIsValid(lastValidSettings))
{
HotkeySettings = lastValidSettings.Clone();
HotkeySettings = lastValidSettings with { };
}
PreviewKeysControl.ItemsSource = hotkeySettings.GetKeysList();

View File

@@ -109,7 +109,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
else
{
_isEnabled = GeneralSettingsConfig.Enabled.PowerOCR;
_isEnabled = GeneralSettingsConfig.Enabled.PowerOcr;
}
}
@@ -129,8 +129,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
_isEnabled = value;
OnPropertyChanged(nameof(IsEnabled));
// Set the status of PowerOCR in the general settings
GeneralSettingsConfig.Enabled.PowerOCR = value;
// Set the status of PowerOcr in the general settings
GeneralSettingsConfig.Enabled.PowerOcr = value;
var outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig);
SendConfigMSG(outgoing.ToString());