diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 46df0235d8..672616c8e7 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1483,6 +1483,7 @@ rgh rgn rgs rguid +rhk RIDEV RIGHTSCROLLBAR riid @@ -1588,6 +1589,7 @@ SHGDNF SHGFI SHIL shinfo +shk shlwapi shobjidl SHORTCUTATLEAST @@ -1798,6 +1800,7 @@ tlbimp tlc tmain TNP +toolgood Toolhelp toolwindow TOPDOWNDIB diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json index 83289fa102..e3ebffc20c 100644 --- a/.pipelines/ESRPSigning_core.json +++ b/.pipelines/ESRPSigning_core.json @@ -60,6 +60,8 @@ "PowerToys.FancyZonesEditorCommon.dll", "PowerToys.FancyZonesModuleInterface.dll", "PowerToys.FancyZones.exe", + "FancyZonesCLI.exe", + "FancyZonesCLI.dll", "PowerToys.GcodePreviewHandler.dll", "PowerToys.GcodePreviewHandler.exe", @@ -351,6 +353,11 @@ "Microsoft.SemanticKernel.Connectors.Ollama.dll", "OllamaSharp.dll", + "boost_regex-vc143-mt-gd-x32-1_87.dll", + "boost_regex-vc143-mt-gd-x64-1_87.dll", + "boost_regex-vc143-mt-x32-1_87.dll", + "boost_regex-vc143-mt-x64-1_87.dll", + "UnitsNet.dll", "UtfUnknown.dll", "Wpf.Ui.dll" diff --git a/.pipelines/versionAndSignCheck.ps1 b/.pipelines/versionAndSignCheck.ps1 index 1bb271300d..f90e59afd6 100644 --- a/.pipelines/versionAndSignCheck.ps1 +++ b/.pipelines/versionAndSignCheck.ps1 @@ -52,7 +52,12 @@ $nullVersionExceptions = @( "System.Diagnostics.EventLog.Messages.dll", "Microsoft.Windows.Widgets.dll", "AdaptiveCards.ObjectModel.WinUI3.dll", - "AdaptiveCards.Rendering.WinUI3.dll") -join '|'; + "AdaptiveCards.Rendering.WinUI3.dll", + "boost_regex_vc143_mt_gd_x32_1_87.dll", + "boost_regex_vc143_mt_gd_x64_1_87.dll", + "boost_regex_vc143_mt_x32_1_87.dll", + "boost_regex_vc143_mt_x64_1_87.dll" + ) -join '|'; $totalFailure = 0; Write-Host $DirPath; diff --git a/Directory.Packages.props b/Directory.Packages.props index 00db38f4df..3d64052a21 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -73,10 +73,10 @@ - - - - + + + + @@ -115,6 +115,7 @@ + @@ -122,6 +123,7 @@ + diff --git a/NOTICE.md b/NOTICE.md index 6ca3cbfceb..23efb64864 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -75,6 +75,37 @@ OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to ``` +### ToolGood.Words.Pinyin + +We use the ToolGood.Words.Pinyin NuGet package for converting Chinese characters to pinyin. + +**Source**: [https://github.com/toolgood/ToolGood.Words.Pinyin](https://github.com/toolgood/ToolGood.Words.Pinyin) + +``` +MIT License + +Copyright (c) 2020 ToolGood + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + + ## Utility: Command Palette Built-in Extensions ### Calculator @@ -1532,6 +1563,7 @@ SOFTWARE. - SkiaSharp.Views.WinUI - StreamJsonRpc - StyleCop.Analyzers +- ToolGood.Words.Pinyin - UnicodeInformation - UnitsNet - UTF.Unknown diff --git a/PowerToys.slnx b/PowerToys.slnx index c946514fb5..1884b2d58b 100644 --- a/PowerToys.slnx +++ b/PowerToys.slnx @@ -370,6 +370,10 @@ + + + + diff --git a/doc/devdocs/core/settings/settings-implementation.md b/doc/devdocs/core/settings/settings-implementation.md index defe59a3fa..65d0d27c73 100644 --- a/doc/devdocs/core/settings/settings-implementation.md +++ b/doc/devdocs/core/settings/settings-implementation.md @@ -38,7 +38,7 @@ For C# modules, the settings are accessed through the `SettingsUtils` class in t using Microsoft.PowerToys.Settings.UI.Library; // Read settings -var settings = SettingsUtils.GetSettings("ModuleName"); +var settings = SettingsUtils.Default.GetSettings("ModuleName"); bool enabled = settings.Enabled; ``` @@ -49,7 +49,7 @@ using Microsoft.PowerToys.Settings.UI.Library; // Write settings settings.Enabled = true; -SettingsUtils.SaveSettings(settings.ToJsonString(), "ModuleName"); +SettingsUtils.Default.SaveSettings(settings.ToJsonString(), "ModuleName"); ``` ## Settings Handling in Modules diff --git a/src/common/UITestAutomation/SettingsConfigHelper.cs b/src/common/UITestAutomation/SettingsConfigHelper.cs index 833ec4f19d..0a01891dc4 100644 --- a/src/common/UITestAutomation/SettingsConfigHelper.cs +++ b/src/common/UITestAutomation/SettingsConfigHelper.cs @@ -21,7 +21,7 @@ namespace Microsoft.PowerToys.UITest public class SettingsConfigHelper { private static readonly JsonSerializerOptions IndentedJsonOptions = new() { WriteIndented = true }; - private static readonly SettingsUtils SettingsUtils = new SettingsUtils(); + private static readonly SettingsUtils SettingsUtils = SettingsUtils.Default; /// /// Configures global PowerToys settings to enable only specified modules and disable all others. diff --git a/src/common/utils/registry.h b/src/common/utils/registry.h index 059589352d..c9770bbea3 100644 --- a/src/common/utils/registry.h +++ b/src/common/utils/registry.h @@ -16,9 +16,54 @@ namespace registry { + namespace detail + { + struct on_exit + { + std::function f; + + on_exit(std::function f) : + f{ std::move(f) } {} + ~on_exit() { f(); } + }; + + template + struct overloaded : Ts... + { + using Ts::operator()...; + }; + + template + overloaded(Ts...) -> overloaded; + + inline const wchar_t* getScopeName(HKEY scope) + { + if (scope == HKEY_LOCAL_MACHINE) + { + return L"HKLM"; + } + else if (scope == HKEY_CURRENT_USER) + { + return L"HKCU"; + } + else if (scope == HKEY_CLASSES_ROOT) + { + return L"HKCR"; + } + else + { + return L"HK??"; + } + } + } + namespace install_scope { const wchar_t INSTALL_SCOPE_REG_KEY[] = L"Software\\Classes\\powertoys\\"; + const wchar_t UNINSTALL_REG_KEY[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; + + // Bundle UpgradeCode from PowerToys.wxs (with braces as stored in registry) + const wchar_t BUNDLE_UPGRADE_CODE[] = L"{6341382D-C0A9-4238-9188-BE9607E3FAB2}"; enum class InstallScope { @@ -26,8 +71,67 @@ namespace registry PerUser, }; + // Helper function to find PowerToys bundle in Windows Uninstall registry by BundleUpgradeCode + inline bool find_powertoys_bundle_in_uninstall_registry(HKEY rootKey) + { + HKEY uninstallKey{}; + if (RegOpenKeyExW(rootKey, UNINSTALL_REG_KEY, 0, KEY_READ, &uninstallKey) != ERROR_SUCCESS) + { + return false; + } + detail::on_exit closeUninstallKey{ [uninstallKey] { RegCloseKey(uninstallKey); } }; + + DWORD index = 0; + wchar_t subKeyName[256]; + + // Enumerate all subkeys under Uninstall + while (RegEnumKeyW(uninstallKey, index++, subKeyName, 256) == ERROR_SUCCESS) + { + HKEY productKey{}; + if (RegOpenKeyExW(uninstallKey, subKeyName, 0, KEY_READ, &productKey) != ERROR_SUCCESS) + { + continue; + } + detail::on_exit closeProductKey{ [productKey] { RegCloseKey(productKey); } }; + + // Check BundleUpgradeCode value (specific to WiX Bundle installations) + wchar_t bundleUpgradeCode[256]{}; + DWORD bundleUpgradeCodeSize = sizeof(bundleUpgradeCode); + + if (RegQueryValueExW(productKey, L"BundleUpgradeCode", nullptr, nullptr, + reinterpret_cast(bundleUpgradeCode), &bundleUpgradeCodeSize) == ERROR_SUCCESS) + { + if (_wcsicmp(bundleUpgradeCode, BUNDLE_UPGRADE_CODE) == 0) + { + return true; + } + } + } + + return false; + } + inline const InstallScope get_current_install_scope() { + // 1. Check HKCU Uninstall registry first (user-level bundle) + // Note: MSI components are always in HKLM regardless of install scope, + // but the Bundle entry will be in HKCU for per-user installations + if (find_powertoys_bundle_in_uninstall_registry(HKEY_CURRENT_USER)) + { + Logger::info(L"Found user-level PowerToys bundle via BundleUpgradeCode in HKCU"); + return InstallScope::PerUser; + } + + // 2. Check HKLM Uninstall registry (machine-level bundle) + if (find_powertoys_bundle_in_uninstall_registry(HKEY_LOCAL_MACHINE)) + { + Logger::info(L"Found machine-level PowerToys bundle via BundleUpgradeCode in HKLM"); + return InstallScope::PerMachine; + } + + // 3. Fallback to legacy custom registry key detection + Logger::info(L"PowerToys bundle not found in Uninstall registry, falling back to legacy detection"); + // Open HKLM key HKEY perMachineKey{}; if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, @@ -45,6 +149,7 @@ namespace registry &perUserKey) != ERROR_SUCCESS) { // both keys are missing + Logger::warn(L"No PowerToys installation detected, defaulting to PerMachine"); return InstallScope::PerMachine; } else @@ -96,47 +201,6 @@ namespace registry template inline constexpr bool always_false_v = false; - namespace detail - { - struct on_exit - { - std::function f; - - on_exit(std::function f) : - f{ std::move(f) } {} - ~on_exit() { f(); } - }; - - template - struct overloaded : Ts... - { - using Ts::operator()...; - }; - - template - overloaded(Ts...) -> overloaded; - - inline const wchar_t* getScopeName(HKEY scope) - { - if (scope == HKEY_LOCAL_MACHINE) - { - return L"HKLM"; - } - else if (scope == HKEY_CURRENT_USER) - { - return L"HKCU"; - } - else if (scope == HKEY_CLASSES_ROOT) - { - return L"HKCR"; - } - else - { - return L"HK??"; - } - } - } - struct ValueChange { using value_t = std::variant; diff --git a/src/dsc/v3/PowerToys.DSC.UnitTests/SettingsResourceTests/SettingsResourceModuleTest`1.cs b/src/dsc/v3/PowerToys.DSC.UnitTests/SettingsResourceTests/SettingsResourceModuleTest`1.cs index ad7eb1d200..8d43f48a77 100644 --- a/src/dsc/v3/PowerToys.DSC.UnitTests/SettingsResourceTests/SettingsResourceModuleTest`1.cs +++ b/src/dsc/v3/PowerToys.DSC.UnitTests/SettingsResourceTests/SettingsResourceModuleTest`1.cs @@ -18,7 +18,7 @@ namespace PowerToys.DSC.UnitTests.SettingsResourceTests; public abstract class SettingsResourceModuleTest : BaseDscTest where TSettingsConfig : ISettingsConfig, new() { - private readonly SettingsUtils _settingsUtils = new(); + private readonly SettingsUtils _settingsUtils = SettingsUtils.Default; private TSettingsConfig _originalSettings; protected TSettingsConfig DefaultSettings => new(); diff --git a/src/dsc/v3/PowerToys.DSC/Models/FunctionData/SettingsFunctionData`1.cs b/src/dsc/v3/PowerToys.DSC/Models/FunctionData/SettingsFunctionData`1.cs index 7fcce03d33..9d87b1e773 100644 --- a/src/dsc/v3/PowerToys.DSC/Models/FunctionData/SettingsFunctionData`1.cs +++ b/src/dsc/v3/PowerToys.DSC/Models/FunctionData/SettingsFunctionData`1.cs @@ -18,7 +18,7 @@ namespace PowerToys.DSC.Models.FunctionData; public sealed class SettingsFunctionData : BaseFunctionData, ISettingsFunctionData where TSettingsConfig : ISettingsConfig, new() { - private static readonly SettingsUtils _settingsUtils = new(); + private static readonly SettingsUtils _settingsUtils = SettingsUtils.Default; private static readonly TSettingsConfig _settingsConfig = new(); private readonly SettingsResourceObject _input; diff --git a/src/modules/Hosts/Hosts/Settings/UserSettings.cs b/src/modules/Hosts/Hosts/Settings/UserSettings.cs index 038823f0e2..bd69a336eb 100644 --- a/src/modules/Hosts/Hosts/Settings/UserSettings.cs +++ b/src/modules/Hosts/Hosts/Settings/UserSettings.cs @@ -62,7 +62,7 @@ namespace Hosts.Settings public UserSettings() { - _settingsUtils = new SettingsUtils(); + _settingsUtils = SettingsUtils.Default; var defaultSettings = new HostsProperties(); ShowStartupWarning = defaultSettings.ShowStartupWarning; LoopbackDuplicates = defaultSettings.LoopbackDuplicates; diff --git a/src/modules/MeasureTool/MeasureToolUI/Settings.cs b/src/modules/MeasureTool/MeasureToolUI/Settings.cs index 4e8cd99b18..ac48339ad6 100644 --- a/src/modules/MeasureTool/MeasureToolUI/Settings.cs +++ b/src/modules/MeasureTool/MeasureToolUI/Settings.cs @@ -11,7 +11,7 @@ namespace MeasureToolUI { public sealed class Settings { - private static readonly SettingsUtils ModuleSettings = new(); + private static readonly SettingsUtils ModuleSettings = SettingsUtils.Default; public MeasureToolMeasureStyle DefaultMeasureStyle { diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/SettingsHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/SettingsHelper.cs index efe721e873..6e19043547 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Helpers/SettingsHelper.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/SettingsHelper.cs @@ -53,7 +53,7 @@ internal sealed class SettingsHelper lock (this.LockObject) { { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; // set this to 1 to disable retries var remainingRetries = 5; diff --git a/src/modules/MouseWithoutBorders/App/Class/Setting.cs b/src/modules/MouseWithoutBorders/App/Class/Setting.cs index c526c70976..7d440a6125 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Setting.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Setting.cs @@ -192,7 +192,7 @@ namespace MouseWithoutBorders.Class internal Settings() { - _settingsUtils = new SettingsUtils(); + _settingsUtils = SettingsUtils.Default; _watcher = SettingsHelper.GetFileWatcher("MouseWithoutBorders", "settings.json", () => { diff --git a/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs b/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs index 0c57171f3a..053f3b5f30 100644 --- a/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs +++ b/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs @@ -29,7 +29,7 @@ namespace PowerOCR.Settings [ImportingConstructor] public UserSettings(Helpers.IThrottledActionInvoker throttledActionInvoker) { - _settingsUtils = new SettingsUtils(); + _settingsUtils = SettingsUtils.Default; ActivationShortcut = new SettingItem(DefaultActivationShortcut); PreferredLanguage = new SettingItem(string.Empty); diff --git a/src/modules/Workspaces/WorkspacesEditor/Utils/Settings.cs b/src/modules/Workspaces/WorkspacesEditor/Utils/Settings.cs index 29dd65d56f..0955e4019e 100644 --- a/src/modules/Workspaces/WorkspacesEditor/Utils/Settings.cs +++ b/src/modules/Workspaces/WorkspacesEditor/Utils/Settings.cs @@ -9,7 +9,7 @@ namespace WorkspacesEditor.Utils public class Settings { private const string WorkspacesModuleName = "Workspaces"; - private static readonly SettingsUtils _settingsUtils = new(); + private static readonly SettingsUtils _settingsUtils = SettingsUtils.Default; public static WorkspacesSettings ReadSettings() { diff --git a/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs b/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs index d05498d62e..30b28f4cd8 100644 --- a/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs +++ b/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs @@ -133,7 +133,7 @@ namespace WorkspacesEditor.ViewModels _orderByIndex = value; OnPropertyChanged(new PropertyChangedEventArgs(nameof(WorkspacesView))); settings.Properties.SortBy = (WorkspacesProperties.SortByProperty)value; - settings.Save(new SettingsUtils()); + settings.Save(SettingsUtils.Default); } } diff --git a/src/modules/awake/Awake/Core/Manager.cs b/src/modules/awake/Awake/Core/Manager.cs index c6aa1c2efb..cc3e461b20 100644 --- a/src/modules/awake/Awake/Core/Manager.cs +++ b/src/modules/awake/Awake/Core/Manager.cs @@ -60,7 +60,7 @@ namespace Awake.Core { _tokenSource = new CancellationTokenSource(); _stateQueue = []; - ModuleSettings = new SettingsUtils(); + ModuleSettings = SettingsUtils.Default; } internal static void StartMonitor() diff --git a/src/modules/awake/Awake/Program.cs b/src/modules/awake/Awake/Program.cs index 4d6d20bc96..b5c3102ba0 100644 --- a/src/modules/awake/Awake/Program.cs +++ b/src/modules/awake/Awake/Program.cs @@ -51,7 +51,7 @@ namespace Awake private static async Task Main(string[] args) { - _settingsUtils = new SettingsUtils(); + _settingsUtils = SettingsUtils.Default; LockMutex = new Mutex(true, Core.Constants.AppName, out bool instantiated); diff --git a/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Messages/NavigateLeftCommand.cs b/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Messages/NavigateLeftCommand.cs new file mode 100644 index 0000000000..d352b552cf --- /dev/null +++ b/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Messages/NavigateLeftCommand.cs @@ -0,0 +1,10 @@ +// 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. + +namespace Microsoft.CmdPal.Core.ViewModels.Messages; + +/// +/// Used to navigate left in a grid view when pressing the Left arrow key in the SearchBox. +/// +public record NavigateLeftCommand; diff --git a/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Messages/NavigateRightCommand.cs b/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Messages/NavigateRightCommand.cs new file mode 100644 index 0000000000..3cfb05913d --- /dev/null +++ b/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Messages/NavigateRightCommand.cs @@ -0,0 +1,10 @@ +// 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. + +namespace Microsoft.CmdPal.Core.ViewModels.Messages; + +/// +/// Used to navigate right in a grid view when pressing the Right arrow key in the SearchBox. +/// +public record NavigateRightCommand; diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/MainListPage.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/MainListPage.cs index 003a0bfb9e..4118ac64db 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/MainListPage.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/MainListPage.cs @@ -12,6 +12,7 @@ using Microsoft.CmdPal.Core.ViewModels.Messages; using Microsoft.CmdPal.Ext.Apps; using Microsoft.CmdPal.Ext.Apps.Programs; using Microsoft.CmdPal.Ext.Apps.State; +using Microsoft.CmdPal.UI.ViewModels.Commands; using Microsoft.CmdPal.UI.ViewModels.Messages; using Microsoft.CmdPal.UI.ViewModels.Properties; using Microsoft.CommandPalette.Extensions; @@ -44,6 +45,9 @@ public partial class MainListPage : DynamicListPage, private List>? _filteredItems; private List>? _filteredApps; private List>? _fallbackItems; + + // Keep as IEnumerable for deferred execution. Fallback item titles are updated + // asynchronously, so scoring must happen lazily when GetItems is called. private IEnumerable>? _scoredFallbackItems; private bool _includeApps; private bool _filteredItemsIncludesApps; @@ -155,42 +159,18 @@ public partial class MainListPage : DynamicListPage, public override IListItem[] GetItems() { - if (string.IsNullOrEmpty(SearchText)) + lock (_tlcManager.TopLevelCommands) { - lock (_tlcManager.TopLevelCommands) - { - return _tlcManager - .TopLevelCommands - .Where(tlc => !tlc.IsFallback && !string.IsNullOrEmpty(tlc.Title)) - .ToArray(); - } - } - else - { - lock (_tlcManager.TopLevelCommands) - { - var limitedApps = new List>(); - - // Fuzzy matching can produce a lot of results, so we want to limit the - // number of apps we show at once if it's a large set. - if (_filteredApps?.Count > 0) - { - limitedApps = _filteredApps.OrderByDescending(s => s.Score).Take(_appResultLimit).ToList(); - } - - var items = Enumerable.Empty>() - .Concat(_filteredItems is not null ? _filteredItems : []) - .Concat(_scoredFallbackItems is not null ? _scoredFallbackItems : []) - .Concat(limitedApps) - .OrderByDescending(o => o.Score) - - // Add fallback items post-sort so they are always at the end of the list - // and eventually ordered based on user preference - .Concat(_fallbackItems is not null ? _fallbackItems.Where(w => !string.IsNullOrEmpty(w.Item.Title)) : []) - .Select(s => s.Item) - .ToArray(); - return items; - } + // Either return the top-level commands (no search text), or the merged and + // filtered results. + return string.IsNullOrEmpty(SearchText) + ? _tlcManager.TopLevelCommands.Where(tlc => !tlc.IsFallback && !string.IsNullOrEmpty(tlc.Title)).ToArray() + : MainListPageResultFactory.Create( + _filteredItems, + _scoredFallbackItems?.ToList(), + _filteredApps, + _fallbackItems, + _appResultLimit); } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/MainListPageResultFactory.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/MainListPageResultFactory.cs new file mode 100644 index 0000000000..f1bddf5197 --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/MainListPageResultFactory.cs @@ -0,0 +1,156 @@ +// 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. + +#pragma warning disable IDE0007 // Use implicit type + +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace Microsoft.CmdPal.UI.ViewModels.Commands; + +internal static class MainListPageResultFactory +{ + /// + /// Creates a merged and ordered array of results from multiple scored input lists, + /// applying an application result limit and filtering fallback items as needed. + /// + public static IListItem[] Create( + IList>? filteredItems, + IList>? scoredFallbackItems, + IList>? filteredApps, + IList>? fallbackItems, + int appResultLimit) + { + if (appResultLimit < 0) + { + throw new ArgumentOutOfRangeException( + nameof(appResultLimit), "App result limit must be non-negative."); + } + + int len1 = filteredItems?.Count ?? 0; + int len2 = scoredFallbackItems?.Count ?? 0; + + // Apps are pre-sorted, so we just need to take the top N, limited by appResultLimit. + int len3 = Math.Min(filteredApps?.Count ?? 0, appResultLimit); + + // Allocate the exact size of the result array. + int totalCount = len1 + len2 + len3 + GetNonEmptyFallbackItemsCount(fallbackItems); + var result = new IListItem[totalCount]; + + // Three-way stable merge of already-sorted lists. + int idx1 = 0, idx2 = 0, idx3 = 0; + int writePos = 0; + + // Merge while all three lists have items. To maintain a stable sort, the + // priority is: list1 > list2 > list3 when scores are equal. + while (idx1 < len1 && idx2 < len2 && idx3 < len3) + { + // Using null-forgiving operator as we have already checked against lengths. + int score1 = filteredItems![idx1].Score; + int score2 = scoredFallbackItems![idx2].Score; + int score3 = filteredApps![idx3].Score; + + if (score1 >= score2 && score1 >= score3) + { + result[writePos++] = filteredItems[idx1++].Item; + } + else if (score2 >= score3) + { + result[writePos++] = scoredFallbackItems[idx2++].Item; + } + else + { + result[writePos++] = filteredApps[idx3++].Item; + } + } + + // Two-way merges for remaining pairs. + while (idx1 < len1 && idx2 < len2) + { + if (filteredItems![idx1].Score >= scoredFallbackItems![idx2].Score) + { + result[writePos++] = filteredItems[idx1++].Item; + } + else + { + result[writePos++] = scoredFallbackItems[idx2++].Item; + } + } + + while (idx1 < len1 && idx3 < len3) + { + if (filteredItems![idx1].Score >= filteredApps![idx3].Score) + { + result[writePos++] = filteredItems[idx1++].Item; + } + else + { + result[writePos++] = filteredApps[idx3++].Item; + } + } + + while (idx2 < len2 && idx3 < len3) + { + if (scoredFallbackItems![idx2].Score >= filteredApps![idx3].Score) + { + result[writePos++] = scoredFallbackItems[idx2++].Item; + } + else + { + result[writePos++] = filteredApps[idx3++].Item; + } + } + + // Drain remaining items from a non-empty list. + while (idx1 < len1) + { + result[writePos++] = filteredItems![idx1++].Item; + } + + while (idx2 < len2) + { + result[writePos++] = scoredFallbackItems![idx2++].Item; + } + + while (idx3 < len3) + { + result[writePos++] = filteredApps![idx3++].Item; + } + + // Append filtered fallback items. Fallback items are added post-sort so they are + // always at the end of the list and eventually ordered based on user preference. + if (fallbackItems is not null) + { + for (int i = 0; i < fallbackItems.Count; i++) + { + var item = fallbackItems[i].Item; + if (!string.IsNullOrEmpty(item.Title)) + { + result[writePos++] = item; + } + } + } + + return result; + } + + private static int GetNonEmptyFallbackItemsCount(IList>? fallbackItems) + { + int fallbackItemsCount = 0; + + if (fallbackItems is not null) + { + for (int i = 0; i < fallbackItems.Count; i++) + { + if (!string.IsNullOrEmpty(fallbackItems[i].Item.Title)) + { + fallbackItemsCount++; + } + } + } + + return fallbackItemsCount; + } +} +#pragma warning restore IDE0007 // Use implicit type diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/SearchBar.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/SearchBar.xaml.cs index 169b34a8b0..0d6fd58afa 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/SearchBar.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/SearchBar.xaml.cs @@ -208,21 +208,32 @@ public sealed partial class SearchBar : UserControl, e.Handled = true; } + else if (e.Key == VirtualKey.Left) + { + // Check if we're in a grid view, and if so, send grid navigation command + var isGridView = CurrentPageViewModel is ListViewModel { IsGridView: true }; + + // Special handling is required if we're in grid view. + if (isGridView) + { + WeakReferenceMessenger.Default.Send(); + e.Handled = true; + } + } else if (e.Key == VirtualKey.Right) { // Check if the "replace search text with suggestion" feature from 0.4-0.5 is enabled. // If it isn't, then only use the suggestion when the caret is at the end of the input. if (!IsTextToSuggestEnabled) { - if (_textToSuggest != null && + if (!string.IsNullOrEmpty(_textToSuggest) && FilterBox.SelectionStart == FilterBox.Text.Length) { FilterBox.Text = _textToSuggest; FilterBox.Select(_textToSuggest.Length, 0); e.Handled = true; + return; } - - return; } // Here, we're using the "replace search text with suggestion" feature. @@ -232,6 +243,20 @@ public sealed partial class SearchBar : UserControl, _lastText = null; DoFilterBoxUpdate(); } + + // Wouldn't want to perform text completion *and* move the selected item, so only perform this if text suggestion wasn't performed. + if (!e.Handled) + { + // Check if we're in a grid view, and if so, send grid navigation command + var isGridView = CurrentPageViewModel is ListViewModel { IsGridView: true }; + + // Special handling is required if we're in grid view. + if (isGridView) + { + WeakReferenceMessenger.Default.Send(); + e.Handled = true; + } + } } else if (e.Key == VirtualKey.Down) { @@ -274,6 +299,8 @@ public sealed partial class SearchBar : UserControl, e.Key == VirtualKey.Up || e.Key == VirtualKey.Down || + e.Key == VirtualKey.Left || + e.Key == VirtualKey.Right || e.Key == VirtualKey.RightMenu || e.Key == VirtualKey.LeftMenu || diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/ExtViews/ListPage.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/ExtViews/ListPage.xaml.cs index a28ae3e133..8957f63ea4 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/ExtViews/ListPage.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/ExtViews/ListPage.xaml.cs @@ -26,6 +26,8 @@ namespace Microsoft.CmdPal.UI; public sealed partial class ListPage : Page, IRecipient, IRecipient, + IRecipient, + IRecipient, IRecipient, IRecipient, IRecipient, @@ -85,6 +87,8 @@ public sealed partial class ListPage : Page, // RegisterAll isn't AOT compatible WeakReferenceMessenger.Default.Register(this); WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); WeakReferenceMessenger.Default.Register(this); WeakReferenceMessenger.Default.Register(this); WeakReferenceMessenger.Default.Register(this); @@ -99,6 +103,8 @@ public sealed partial class ListPage : Page, WeakReferenceMessenger.Default.Unregister(this); WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); WeakReferenceMessenger.Default.Unregister(this); WeakReferenceMessenger.Default.Unregister(this); WeakReferenceMessenger.Default.Unregister(this); @@ -257,25 +263,71 @@ public sealed partial class ListPage : Page, // And then have these commands manipulate that state being bound to the UI instead // We may want to see how other non-list UIs need to behave to make this decision // At least it's decoupled from the SearchBox now :) - if (ItemView.SelectedIndex < ItemView.Items.Count - 1) + if (ViewModel?.IsGridView == true) { - ItemView.SelectedIndex++; + // For grid views, use spatial navigation (down) + HandleGridArrowNavigation(VirtualKey.Down); } else { - ItemView.SelectedIndex = 0; + // For list views, use simple linear navigation + if (ItemView.SelectedIndex < ItemView.Items.Count - 1) + { + ItemView.SelectedIndex++; + } + else + { + ItemView.SelectedIndex = 0; + } } } public void Receive(NavigatePreviousCommand message) { - if (ItemView.SelectedIndex > 0) + if (ViewModel?.IsGridView == true) { - ItemView.SelectedIndex--; + // For grid views, use spatial navigation (up) + HandleGridArrowNavigation(VirtualKey.Up); } else { - ItemView.SelectedIndex = ItemView.Items.Count - 1; + // For list views, use simple linear navigation + if (ItemView.SelectedIndex > 0) + { + ItemView.SelectedIndex--; + } + else + { + ItemView.SelectedIndex = ItemView.Items.Count - 1; + } + } + } + + public void Receive(NavigateLeftCommand message) + { + // For grid views, use spatial navigation. For list views, just move up. + if (ViewModel?.IsGridView == true) + { + HandleGridArrowNavigation(VirtualKey.Left); + } + else + { + // In list view, left arrow doesn't navigate + // This maintains consistency with the SearchBar behavior + } + } + + public void Receive(NavigateRightCommand message) + { + // For grid views, use spatial navigation. For list views, just move down. + if (ViewModel?.IsGridView == true) + { + HandleGridArrowNavigation(VirtualKey.Right); + } + else + { + // In list view, right arrow doesn't navigate + // This maintains consistency with the SearchBar behavior } } @@ -514,6 +566,130 @@ public sealed partial class ListPage : Page, return null; } + // Find a logical neighbor in the requested direction using containers' positions. + private void HandleGridArrowNavigation(VirtualKey key) + { + if (ItemView.Items.Count == 0) + { + // No items, goodbye. + return; + } + + var currentIndex = ItemView.SelectedIndex; + if (currentIndex < 0) + { + // -1 is a valid value (no item currently selected) + currentIndex = 0; + ItemView.SelectedIndex = 0; + } + + try + { + // Try to compute using container positions; if not available, fall back to simple +/-1. + var currentContainer = ItemView.ContainerFromIndex(currentIndex) as FrameworkElement; + if (currentContainer is not null && currentContainer.ActualWidth != 0 && currentContainer.ActualHeight != 0) + { + // Use center of current container as reference + var curPoint = currentContainer.TransformToVisual(ItemView).TransformPoint(new Point(0, 0)); + var curCenterX = curPoint.X + (currentContainer.ActualWidth / 2.0); + var curCenterY = curPoint.Y + (currentContainer.ActualHeight / 2.0); + + var bestScore = double.MaxValue; + var bestIndex = currentIndex; + + for (var i = 0; i < ItemView.Items.Count; i++) + { + if (i == currentIndex) + { + continue; + } + + if (ItemView.ContainerFromIndex(i) is FrameworkElement c && c.ActualWidth > 0 && c.ActualHeight > 0) + { + var p = c.TransformToVisual(ItemView).TransformPoint(new Point(0, 0)); + var centerX = p.X + (c.ActualWidth / 2.0); + var centerY = p.Y + (c.ActualHeight / 2.0); + + var dx = centerX - curCenterX; + var dy = centerY - curCenterY; + + var candidate = false; + var score = double.MaxValue; + + switch (key) + { + case VirtualKey.Left: + if (dx < 0) + { + candidate = true; + score = Math.Abs(dy) + (Math.Abs(dx) * 0.7); + } + + break; + case VirtualKey.Right: + if (dx > 0) + { + candidate = true; + score = Math.Abs(dy) + (Math.Abs(dx) * 0.7); + } + + break; + case VirtualKey.Up: + if (dy < 0) + { + candidate = true; + score = Math.Abs(dx) + (Math.Abs(dy) * 0.7); + } + + break; + case VirtualKey.Down: + if (dy > 0) + { + candidate = true; + score = Math.Abs(dx) + (Math.Abs(dy) * 0.7); + } + + break; + } + + if (candidate && score < bestScore) + { + bestScore = score; + bestIndex = i; + } + } + } + + if (bestIndex != currentIndex) + { + ItemView.SelectedIndex = bestIndex; + ItemView.ScrollIntoView(ItemView.SelectedItem); + } + + return; + } + } + catch + { + // ignore transform errors and fall back + } + + // fallback linear behavior + var fallback = key switch + { + VirtualKey.Left => Math.Max(0, currentIndex - 1), + VirtualKey.Right => Math.Min(ItemView.Items.Count - 1, currentIndex + 1), + VirtualKey.Up => Math.Max(0, currentIndex - 1), + VirtualKey.Down => Math.Min(ItemView.Items.Count - 1, currentIndex + 1), + _ => currentIndex, + }; + if (fallback != currentIndex) + { + ItemView.SelectedIndex = fallback; + ItemView.ScrollIntoView(ItemView.SelectedItem); + } + } + private void Items_OnContextRequested(UIElement sender, ContextRequestedEventArgs e) { var (item, element) = e.OriginalSource switch @@ -564,9 +740,27 @@ public sealed partial class ListPage : Page, private void Items_PreviewKeyDown(object sender, KeyRoutedEventArgs e) { + // Track keyboard as the last input source for activation logic. if (e.Key is VirtualKey.Enter or VirtualKey.Space) { _lastInputSource = InputSource.Keyboard; + return; + } + + // Handle arrow navigation when we're showing a grid. + if (ViewModel?.IsGridView == true) + { + switch (e.Key) + { + case VirtualKey.Left: + case VirtualKey.Right: + case VirtualKey.Up: + case VirtualKey.Down: + _lastInputSource = InputSource.Keyboard; + HandleGridArrowNavigation(e.Key); + e.Handled = true; + break; + } } } diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.UI.ViewModels.UnitTests/MainListPageResultFactoryTests.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.UI.ViewModels.UnitTests/MainListPageResultFactoryTests.cs new file mode 100644 index 0000000000..624fa2da73 --- /dev/null +++ b/src/modules/cmdpal/Tests/Microsoft.CmdPal.UI.ViewModels.UnitTests/MainListPageResultFactoryTests.cs @@ -0,0 +1,161 @@ +// 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.Linq; +using Microsoft.CmdPal.UI.ViewModels.Commands; +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Windows.Foundation; + +namespace Microsoft.CmdPal.UI.ViewModels.UnitTests; + +[TestClass] +public partial class MainListPageResultFactoryTests +{ + private sealed partial class MockListItem : IListItem + { + public string Title { get; set; } = string.Empty; + + public string Subtitle { get; set; } = string.Empty; + + public ICommand Command => new NoOpCommand(); + + public IDetails? Details => null; + + public IIconInfo? Icon => null; + + public string Section => throw new NotImplementedException(); + + public ITag[] Tags => throw new NotImplementedException(); + + public string TextToSuggest => throw new NotImplementedException(); + + public IContextItem[] MoreCommands => throw new NotImplementedException(); + +#pragma warning disable CS0067 // The event is never used + public event TypedEventHandler? PropChanged; +#pragma warning restore CS0067 // The event is never used + + public override string ToString() => Title; + } + + private static Scored S(string title, int score) + { + return new Scored + { + Score = score, + Item = new MockListItem { Title = title }, + }; + } + + [TestMethod] + public void Merge_PrioritizesListsCorrectly() + { + var filtered = new List> + { + S("F1", 100), + S("F2", 50), + }; + + var scoredFallback = new List> + { + S("SF1", 100), + S("SF2", 60), + }; + + var apps = new List> + { + S("A1", 100), + S("A2", 55), + }; + + // Fallbacks are not scored. + var fallbacks = new List> + { + S("FB1", 0), + S("FB2", 0), + }; + + var result = MainListPageResultFactory.Create( + filtered, + scoredFallback, + apps, + fallbacks, + appResultLimit: 10); + + // Expected order: + // 100: F1, SF1, A1 + // 60: SF2 + // 55: A2 + // 50: F2 + // Then fallbacks in original order: FB1, FB2 + var titles = result.Select(r => r.Title).ToArray(); +#pragma warning disable CA1861 // Avoid constant arrays as arguments + CollectionAssert.AreEqual( + new[] { "F1", "SF1", "A1", "SF2", "A2", "F2", "FB1", "FB2" }, + titles); +#pragma warning restore CA1861 // Avoid constant arrays as arguments + } + + [TestMethod] + public void Merge_AppliesAppLimit() + { + var apps = new List> + { + S("A1", 100), + S("A2", 90), + S("A3", 80), + }; + + var result = MainListPageResultFactory.Create( + null, + null, + apps, + null, + 2); + + Assert.AreEqual(2, result.Length); + Assert.AreEqual("A1", result[0].Title); + Assert.AreEqual("A2", result[1].Title); + } + + [TestMethod] + public void Merge_FiltersEmptyFallbacks() + { + var fallbacks = new List> + { + S("FB1", 0), + S(string.Empty, 0), + S("FB3", 0), + }; + + var result = MainListPageResultFactory.Create( + null, + null, + null, + fallbacks, + appResultLimit: 10); + + Assert.AreEqual(2, result.Length); + Assert.AreEqual("FB1", result[0].Title); + Assert.AreEqual("FB3", result[1].Title); + } + + [TestMethod] + public void Merge_HandlesNullLists() + { + var result = MainListPageResultFactory.Create( + null, + null, + null, + null, + appResultLimit: 10); + + Assert.IsNotNull(result); + Assert.AreEqual(0, result.Length); + } +} diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/FuzzyStringMatcher.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/FuzzyStringMatcher.cs index f4591bc443..7ecfc74222 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/FuzzyStringMatcher.cs +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/FuzzyStringMatcher.cs @@ -2,6 +2,10 @@ // 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 ToolGood.Words.Pinyin; + namespace Microsoft.CommandPalette.Extensions.Toolkit; // Inspired by the fuzzy.rs from edit.exe @@ -9,6 +13,21 @@ public static class FuzzyStringMatcher { private const int NOMATCH = 0; + /// + /// Gets a value indicating whether to support Chinese PinYin. + /// Automatically enabled when the system UI culture is Simplified Chinese. + /// + public static bool ChinesePinYinSupport { get; } = IsSimplifiedChinese(); + + private static bool IsSimplifiedChinese() + { + var culture = CultureInfo.CurrentUICulture; + + // Detect Simplified Chinese: zh-CN, zh-Hans, zh-Hans-* + return culture.Name.StartsWith("zh-CN", StringComparison.OrdinalIgnoreCase) + || culture.Name.StartsWith("zh-Hans", StringComparison.OrdinalIgnoreCase); + } + public static int ScoreFuzzy(string needle, string haystack, bool allowNonContiguousMatches = true) { var (s, _) = ScoreFuzzyWithPositions(needle, haystack, allowNonContiguousMatches); @@ -16,6 +35,28 @@ public static class FuzzyStringMatcher } public static (int Score, List Positions) ScoreFuzzyWithPositions(string needle, string haystack, bool allowNonContiguousMatches) + => ScoreAllFuzzyWithPositions(needle, haystack, allowNonContiguousMatches).MaxBy(i => i.Score); + + public static IEnumerable<(int Score, List Positions)> ScoreAllFuzzyWithPositions(string needle, string haystack, bool allowNonContiguousMatches) + { + List needles = [needle]; + List haystacks = [haystack]; + + if (ChinesePinYinSupport) + { + // Remove IME composition split characters. + var input = needle.Replace("'", string.Empty); + needles.Add(WordsHelper.GetPinyin(input)); + if (WordsHelper.HasChinese(haystack)) + { + haystacks.Add(WordsHelper.GetPinyin(haystack)); + } + } + + return needles.SelectMany(i => haystacks.Select(j => ScoreFuzzyWithPositionsInternal(i, j, allowNonContiguousMatches))); + } + + private static (int Score, List Positions) ScoreFuzzyWithPositionsInternal(string needle, string haystack, bool allowNonContiguousMatches) { if (string.IsNullOrEmpty(haystack) || string.IsNullOrEmpty(needle)) { diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Microsoft.CommandPalette.Extensions.Toolkit.csproj b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Microsoft.CommandPalette.Extensions.Toolkit.csproj index f5f8f2ccbc..eb887e9e36 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Microsoft.CommandPalette.Extensions.Toolkit.csproj +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Microsoft.CommandPalette.Extensions.Toolkit.csproj @@ -42,6 +42,7 @@ runtime; build; native; contentfiles; analyzers + diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj index a6cad871ab..fb647cc444 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj @@ -4,7 +4,7 @@ ..\..\..\..\..\ $(PathToRoot)packages\Microsoft.WindowsAppSDK.1.8.250907003 $(PathToRoot)packages\Microsoft.Windows.CppWinRT.2.0.240111.5 - $(PathToRoot)packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188 + $(PathToRoot)packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.6901 $(PathToRoot)packages\Microsoft.Web.WebView2.1.0.2903.40 diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/packages.config b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/packages.config index e945c5824d..091ef0782d 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/packages.config +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/packages.config @@ -12,6 +12,6 @@ - + - \ No newline at end of file + diff --git a/src/modules/cmdpal/extensionsdk/nuget/BuildSDKHelper.ps1 b/src/modules/cmdpal/extensionsdk/nuget/BuildSDKHelper.ps1 index 4390f0120e..2afad38df5 100644 --- a/src/modules/cmdpal/extensionsdk/nuget/BuildSDKHelper.ps1 +++ b/src/modules/cmdpal/extensionsdk/nuget/BuildSDKHelper.ps1 @@ -54,9 +54,15 @@ if ($IsAzurePipelineBuild) { } else { $nugetPath = (Join-Path $PSScriptRoot "NugetWrapper.cmd") } +$solutionPath = (Join-Path $PSScriptRoot "..\..\..\..\..\PowerToys.slnx") if (($BuildStep -ieq "all") -Or ($BuildStep -ieq "build")) { - & $nugetPath restore (Join-Path $PSScriptRoot "..\..\..\..\..\PowerToys.slnx") + $restoreArgs = @( + $solutionPath + "/t:Restore" + "/p:RestorePackagesConfig=true" + ) + & $msbuildPath $restoreArgs Try { foreach ($config in $Configuration.Split(",")) { diff --git a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs index e15a792259..dfb5551a87 100644 --- a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs +++ b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs @@ -45,7 +45,7 @@ namespace ColorPicker.Settings [ImportingConstructor] public UserSettings(Helpers.IThrottledActionInvoker throttledActionInvoker) { - _settingsUtils = new SettingsUtils(); + _settingsUtils = SettingsUtils.Default; ChangeCursor = new SettingItem(true); ActivationShortcut = new SettingItem(DefaultActivationShortcut); CopiedColorRepresentation = new SettingItem(ColorRepresentationType.HEX.ToString()); diff --git a/src/modules/fancyzones/FancyZonesCLI/Commands/EditorCommands.cs b/src/modules/fancyzones/FancyZonesCLI/Commands/EditorCommands.cs new file mode 100644 index 0000000000..7bf15dda44 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesCLI/Commands/EditorCommands.cs @@ -0,0 +1,90 @@ +// 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; +using System.IO; +using System.Linq; + +namespace FancyZonesCLI.Commands; + +/// +/// Editor and Settings commands. +/// +internal static class EditorCommands +{ + public static (int ExitCode, string Output) OpenEditor() + { + var editorExe = "PowerToys.FancyZonesEditor.exe"; + + // Check if editor-parameters.json exists + if (!FancyZonesData.EditorParametersExist()) + { + return (1, "Error: editor-parameters.json not found.\nPlease launch FancyZones Editor using Win+` (Win+Backtick) hotkey first."); + } + + // Check if editor is already running + var existingProcess = Process.GetProcessesByName("PowerToys.FancyZonesEditor").FirstOrDefault(); + if (existingProcess != null) + { + NativeMethods.SetForegroundWindow(existingProcess.MainWindowHandle); + return (0, "FancyZones Editor is already running. Brought window to foreground."); + } + + // Only check same directory as CLI + var editorPath = Path.Combine(AppContext.BaseDirectory, editorExe); + + if (File.Exists(editorPath)) + { + try + { + Process.Start(new ProcessStartInfo + { + FileName = editorPath, + UseShellExecute = true, + }); + return (0, "FancyZones Editor launched successfully."); + } + catch (Exception ex) + { + return (1, $"Failed to launch: {ex.Message}"); + } + } + + return (1, $"Error: Could not find {editorExe} in {AppContext.BaseDirectory}"); + } + + public static (int ExitCode, string Output) OpenSettings() + { + try + { + // Find PowerToys.exe in common locations + string powertoysExe = null; + + // Check in the same directory as the CLI (typical for dev builds) + var sameDirPath = Path.Combine(AppContext.BaseDirectory, "PowerToys.exe"); + if (File.Exists(sameDirPath)) + { + powertoysExe = sameDirPath; + } + + if (powertoysExe == null) + { + return (1, "Error: PowerToys.exe not found. Please ensure PowerToys is installed."); + } + + Process.Start(new ProcessStartInfo + { + FileName = powertoysExe, + Arguments = "--open-settings=FancyZones", + UseShellExecute = false, + }); + return (0, "FancyZones Settings opened successfully."); + } + catch (Exception ex) + { + return (1, $"Error: Failed to open FancyZones Settings. {ex.Message}"); + } + } +} diff --git a/src/modules/fancyzones/FancyZonesCLI/Commands/HotkeyCommands.cs b/src/modules/fancyzones/FancyZonesCLI/Commands/HotkeyCommands.cs new file mode 100644 index 0000000000..cfaf93a5d4 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesCLI/Commands/HotkeyCommands.cs @@ -0,0 +1,98 @@ +// 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; + +namespace FancyZonesCLI.Commands; + +/// +/// Hotkey-related commands. +/// +internal static class HotkeyCommands +{ + public static (int ExitCode, string Output) GetHotkeys() + { + var hotkeys = FancyZonesData.ReadLayoutHotkeys(); + if (hotkeys?.Hotkeys == null || hotkeys.Hotkeys.Count == 0) + { + return (0, "No hotkeys configured."); + } + + var sb = new System.Text.StringBuilder(); + sb.AppendLine("=== Layout Hotkeys ===\n"); + sb.AppendLine("Press Win + Ctrl + Alt + to switch layouts:\n"); + + foreach (var hotkey in hotkeys.Hotkeys.OrderBy(h => h.Key)) + { + sb.AppendLine(CultureInfo.InvariantCulture, $" [{hotkey.Key}] => {hotkey.LayoutId}"); + } + + return (0, sb.ToString().TrimEnd()); + } + + public static (int ExitCode, string Output) SetHotkey(int key, string layoutUuid, Action notifyFancyZones, uint wmPrivLayoutHotkeysFileUpdate) + { + if (key < 0 || key > 9) + { + return (1, "Error: Key must be between 0 and 9"); + } + + // Check if this is a custom layout UUID + var customLayouts = FancyZonesData.ReadCustomLayouts(); + var matchedLayout = customLayouts?.Layouts?.FirstOrDefault(l => l.Uuid.Equals(layoutUuid, StringComparison.OrdinalIgnoreCase)); + bool isCustomLayout = matchedLayout != null; + string layoutName = matchedLayout?.Name ?? layoutUuid; + + var hotkeys = FancyZonesData.ReadLayoutHotkeys() ?? new LayoutHotkeys(); + + hotkeys.Hotkeys ??= new List(); + + // Remove existing hotkey for this key + hotkeys.Hotkeys.RemoveAll(h => h.Key == key); + + // Add new hotkey + hotkeys.Hotkeys.Add(new LayoutHotkey { Key = key, LayoutId = layoutUuid }); + + // Save + FancyZonesData.WriteLayoutHotkeys(hotkeys); + + // Notify FancyZones + notifyFancyZones(wmPrivLayoutHotkeysFileUpdate); + + if (isCustomLayout) + { + return (0, $"✓ Hotkey {key} assigned to custom layout '{layoutName}'\n Press Win + Ctrl + Alt + {key} to switch to this layout"); + } + else + { + return (0, $"⚠ Warning: Hotkey {key} assigned to '{layoutUuid}'\n Note: FancyZones hotkeys only work with CUSTOM layouts.\n Template layouts (focus, columns, rows, etc.) cannot be used with hotkeys.\n Create a custom layout in the FancyZones Editor to use this hotkey."); + } + } + + public static (int ExitCode, string Output) RemoveHotkey(int key, Action notifyFancyZones, uint wmPrivLayoutHotkeysFileUpdate) + { + var hotkeys = FancyZonesData.ReadLayoutHotkeys(); + if (hotkeys?.Hotkeys == null) + { + return (0, $"No hotkey assigned to key {key}"); + } + + var removed = hotkeys.Hotkeys.RemoveAll(h => h.Key == key); + if (removed == 0) + { + return (0, $"No hotkey assigned to key {key}"); + } + + // Save + FancyZonesData.WriteLayoutHotkeys(hotkeys); + + // Notify FancyZones + notifyFancyZones(wmPrivLayoutHotkeysFileUpdate); + + return (0, $"Hotkey {key} removed"); + } +} diff --git a/src/modules/fancyzones/FancyZonesCLI/Commands/LayoutCommands.cs b/src/modules/fancyzones/FancyZonesCLI/Commands/LayoutCommands.cs new file mode 100644 index 0000000000..4400b32d46 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesCLI/Commands/LayoutCommands.cs @@ -0,0 +1,276 @@ +// 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.Text.Json; + +namespace FancyZonesCLI.Commands; + +/// +/// Layout-related commands. +/// +internal static class LayoutCommands +{ + public static (int ExitCode, string Output) GetLayouts() + { + var sb = new System.Text.StringBuilder(); + + // Print template layouts + var templatesJson = FancyZonesData.ReadLayoutTemplates(); + if (templatesJson?.Templates != null) + { + sb.AppendLine(CultureInfo.InvariantCulture, $"=== Built-in Template Layouts ({templatesJson.Templates.Count} total) ===\n"); + + for (int i = 0; i < templatesJson.Templates.Count; i++) + { + var template = templatesJson.Templates[i]; + sb.AppendLine(CultureInfo.InvariantCulture, $"[T{i + 1}] {template.Type}"); + sb.Append(CultureInfo.InvariantCulture, $" Zones: {template.ZoneCount}"); + if (template.ShowSpacing && template.Spacing > 0) + { + sb.Append(CultureInfo.InvariantCulture, $", Spacing: {template.Spacing}px"); + } + + sb.AppendLine(); + sb.AppendLine(); + + // Draw visual preview + sb.Append(LayoutVisualizer.DrawTemplateLayout(template)); + + if (i < templatesJson.Templates.Count - 1) + { + sb.AppendLine(); + } + } + + sb.AppendLine("\n"); + } + + // Print custom layouts + var customLayouts = FancyZonesData.ReadCustomLayouts(); + if (customLayouts?.Layouts != null) + { + sb.AppendLine(CultureInfo.InvariantCulture, $"=== Custom Layouts ({customLayouts.Layouts.Count} total) ==="); + + for (int i = 0; i < customLayouts.Layouts.Count; i++) + { + var layout = customLayouts.Layouts[i]; + sb.AppendLine(CultureInfo.InvariantCulture, $"[{i + 1}] {layout.Name}"); + sb.AppendLine(CultureInfo.InvariantCulture, $" UUID: {layout.Uuid}"); + sb.Append(CultureInfo.InvariantCulture, $" Type: {layout.Type}"); + + bool isCanvasLayout = false; + if (layout.Info.ValueKind != JsonValueKind.Undefined && layout.Info.ValueKind != JsonValueKind.Null) + { + if (layout.Type == "grid" && layout.Info.TryGetProperty("rows", out var rows) && layout.Info.TryGetProperty("columns", out var cols)) + { + sb.Append(CultureInfo.InvariantCulture, $" ({rows.GetInt32()}x{cols.GetInt32()} grid)"); + } + else if (layout.Type == "canvas" && layout.Info.TryGetProperty("zones", out var zones)) + { + sb.Append(CultureInfo.InvariantCulture, $" ({zones.GetArrayLength()} zones)"); + isCanvasLayout = true; + } + } + + sb.AppendLine("\n"); + + // Draw visual preview + sb.Append(LayoutVisualizer.DrawCustomLayout(layout)); + + // Add note for canvas layouts + if (isCanvasLayout) + { + sb.AppendLine("\n Note: Canvas layout preview is approximate."); + sb.AppendLine(" Open FancyZones Editor for precise zone boundaries."); + } + + if (i < customLayouts.Layouts.Count - 1) + { + sb.AppendLine(); + } + } + + sb.AppendLine("\nUse 'FancyZonesCLI.exe set-layout ' to apply a layout."); + } + + return (0, sb.ToString().TrimEnd()); + } + + public static (int ExitCode, string Output) GetActiveLayout() + { + if (!FancyZonesData.TryReadAppliedLayouts(out var appliedLayouts, out var error)) + { + return (1, $"Error: {error}"); + } + + if (appliedLayouts.Layouts == null || appliedLayouts.Layouts.Count == 0) + { + return (0, "No active layouts found."); + } + + var sb = new System.Text.StringBuilder(); + sb.AppendLine("\n=== Active FancyZones Layout(s) ===\n"); + + for (int i = 0; i < appliedLayouts.Layouts.Count; i++) + { + var layout = appliedLayouts.Layouts[i]; + sb.AppendLine(CultureInfo.InvariantCulture, $"Monitor {i + 1}:"); + sb.AppendLine(CultureInfo.InvariantCulture, $" Name: {layout.AppliedLayout.Type}"); + sb.AppendLine(CultureInfo.InvariantCulture, $" UUID: {layout.AppliedLayout.Uuid}"); + sb.AppendLine(CultureInfo.InvariantCulture, $" Type: {layout.AppliedLayout.Type} ({layout.AppliedLayout.ZoneCount} zones)"); + + if (layout.AppliedLayout.ShowSpacing) + { + sb.AppendLine(CultureInfo.InvariantCulture, $" Spacing: {layout.AppliedLayout.Spacing}px"); + } + + sb.AppendLine(CultureInfo.InvariantCulture, $" Sensitivity Radius: {layout.AppliedLayout.SensitivityRadius}px"); + + if (i < appliedLayouts.Layouts.Count - 1) + { + sb.AppendLine(); + } + } + + return (0, sb.ToString().TrimEnd()); + } + + public static (int ExitCode, string Output) SetLayout(string[] args, Action notifyFancyZones, uint wmPrivAppliedLayoutsFileUpdate) + { + Logger.LogInfo($"SetLayout called with args: [{string.Join(", ", args)}]"); + + if (args.Length == 0) + { + return (1, "Error: set-layout requires a UUID parameter"); + } + + string uuid = args[0]; + int? targetMonitor = null; + bool applyToAll = false; + + // Parse options + for (int i = 1; i < args.Length; i++) + { + if (args[i] == "--monitor" && i + 1 < args.Length) + { + if (int.TryParse(args[i + 1], out int monitorNum)) + { + targetMonitor = monitorNum; + i++; // Skip next arg + } + else + { + return (1, $"Error: Invalid monitor number: {args[i + 1]}"); + } + } + else if (args[i] == "--all") + { + applyToAll = true; + } + } + + if (targetMonitor.HasValue && applyToAll) + { + return (1, "Error: Cannot specify both --monitor and --all"); + } + + // Try to find layout in custom layouts first (by UUID) + var customLayouts = FancyZonesData.ReadCustomLayouts(); + var targetCustomLayout = customLayouts?.Layouts?.FirstOrDefault(l => l.Uuid.Equals(uuid, StringComparison.OrdinalIgnoreCase)); + + // If not found in custom layouts, try template layouts (by type name) + TemplateLayout targetTemplate = null; + if (targetCustomLayout == null) + { + var templates = FancyZonesData.ReadLayoutTemplates(); + targetTemplate = templates?.Templates?.FirstOrDefault(t => t.Type.Equals(uuid, StringComparison.OrdinalIgnoreCase)); + } + + if (targetCustomLayout == null && targetTemplate == null) + { + return (1, $"Error: Layout '{uuid}' not found\nTip: For templates, use the type name (e.g., 'focus', 'columns', 'rows', 'grid', 'priority-grid')\n For custom layouts, use the UUID from 'get-layouts'"); + } + + // Read current applied layouts + if (!FancyZonesData.TryReadAppliedLayouts(out var appliedLayouts, out var error)) + { + return (1, $"Error: {error}"); + } + + if (appliedLayouts.Layouts == null || appliedLayouts.Layouts.Count == 0) + { + return (1, "Error: No monitors configured"); + } + + // Determine which monitors to update + List monitorsToUpdate = new List(); + if (applyToAll) + { + for (int i = 0; i < appliedLayouts.Layouts.Count; i++) + { + monitorsToUpdate.Add(i); + } + } + else if (targetMonitor.HasValue) + { + int monitorIndex = targetMonitor.Value - 1; // Convert to 0-based + if (monitorIndex < 0 || monitorIndex >= appliedLayouts.Layouts.Count) + { + return (1, $"Error: Monitor {targetMonitor.Value} not found. Available monitors: 1-{appliedLayouts.Layouts.Count}"); + } + + monitorsToUpdate.Add(monitorIndex); + } + else + { + // Default: first monitor + monitorsToUpdate.Add(0); + } + + // Update selected monitors + foreach (int monitorIndex in monitorsToUpdate) + { + if (targetCustomLayout != null) + { + appliedLayouts.Layouts[monitorIndex].AppliedLayout.Uuid = targetCustomLayout.Uuid; + appliedLayouts.Layouts[monitorIndex].AppliedLayout.Type = targetCustomLayout.Type; + } + else if (targetTemplate != null) + { + // For templates, use all-zeros UUID and the template type + appliedLayouts.Layouts[monitorIndex].AppliedLayout.Uuid = "{00000000-0000-0000-0000-000000000000}"; + appliedLayouts.Layouts[monitorIndex].AppliedLayout.Type = targetTemplate.Type; + appliedLayouts.Layouts[monitorIndex].AppliedLayout.ZoneCount = targetTemplate.ZoneCount; + appliedLayouts.Layouts[monitorIndex].AppliedLayout.ShowSpacing = targetTemplate.ShowSpacing; + appliedLayouts.Layouts[monitorIndex].AppliedLayout.Spacing = targetTemplate.Spacing; + } + } + + // Write back to file + FancyZonesData.WriteAppliedLayouts(appliedLayouts); + Logger.LogInfo($"Applied layouts file updated for {monitorsToUpdate.Count} monitor(s)"); + + // Notify FancyZones to reload + notifyFancyZones(wmPrivAppliedLayoutsFileUpdate); + Logger.LogInfo("FancyZones notified of layout change"); + + string layoutName = targetCustomLayout?.Name ?? targetTemplate?.Type ?? uuid; + if (applyToAll) + { + return (0, $"Layout '{layoutName}' applied to all {monitorsToUpdate.Count} monitors"); + } + else if (targetMonitor.HasValue) + { + return (0, $"Layout '{layoutName}' applied to monitor {targetMonitor.Value}"); + } + else + { + return (0, $"Layout '{layoutName}' applied to monitor 1"); + } + } +} diff --git a/src/modules/fancyzones/FancyZonesCLI/Commands/MonitorCommands.cs b/src/modules/fancyzones/FancyZonesCLI/Commands/MonitorCommands.cs new file mode 100644 index 0000000000..f542b901cc --- /dev/null +++ b/src/modules/fancyzones/FancyZonesCLI/Commands/MonitorCommands.cs @@ -0,0 +1,49 @@ +// 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.Globalization; + +namespace FancyZonesCLI.Commands; + +/// +/// Monitor-related commands. +/// +internal static class MonitorCommands +{ + public static (int ExitCode, string Output) GetMonitors() + { + if (!FancyZonesData.TryReadAppliedLayouts(out var appliedLayouts, out var error)) + { + return (1, $"Error: {error}"); + } + + if (appliedLayouts.Layouts == null || appliedLayouts.Layouts.Count == 0) + { + return (0, "No monitors found."); + } + + var sb = new System.Text.StringBuilder(); + sb.AppendLine(CultureInfo.InvariantCulture, $"=== Monitors ({appliedLayouts.Layouts.Count} total) ==="); + sb.AppendLine(); + + for (int i = 0; i < appliedLayouts.Layouts.Count; i++) + { + var layout = appliedLayouts.Layouts[i]; + var monitorNum = i + 1; + + sb.AppendLine(CultureInfo.InvariantCulture, $"Monitor {monitorNum}:"); + sb.AppendLine(CultureInfo.InvariantCulture, $" Monitor: {layout.Device.Monitor}"); + sb.AppendLine(CultureInfo.InvariantCulture, $" Monitor Instance: {layout.Device.MonitorInstance}"); + sb.AppendLine(CultureInfo.InvariantCulture, $" Monitor Number: {layout.Device.MonitorNumber}"); + sb.AppendLine(CultureInfo.InvariantCulture, $" Serial Number: {layout.Device.SerialNumber}"); + sb.AppendLine(CultureInfo.InvariantCulture, $" Virtual Desktop: {layout.Device.VirtualDesktop}"); + sb.AppendLine(CultureInfo.InvariantCulture, $" Sensitivity Radius: {layout.AppliedLayout.SensitivityRadius}px"); + sb.AppendLine(CultureInfo.InvariantCulture, $" Active Layout: {layout.AppliedLayout.Type}"); + sb.AppendLine(CultureInfo.InvariantCulture, $" Zone Count: {layout.AppliedLayout.ZoneCount}"); + sb.AppendLine(); + } + + return (0, sb.ToString().TrimEnd()); + } +} diff --git a/src/modules/fancyzones/FancyZonesCLI/FancyZonesCLI.csproj b/src/modules/fancyzones/FancyZonesCLI/FancyZonesCLI.csproj new file mode 100644 index 0000000000..85c2fa30e5 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesCLI/FancyZonesCLI.csproj @@ -0,0 +1,32 @@ + + + + + + + + PowerToys.FancyZonesCLI + PowerToys FancyZones Command Line Interface + PowerToys FancyZones CLI + Exe + x64;ARM64 + true + true + false + false + ..\..\..\..\$(Platform)\$(Configuration) + FancyZonesCLI + $(NoWarn);SA1500;SA1402;CA1852 + + + + + + + + + + + + + diff --git a/src/modules/fancyzones/FancyZonesCLI/FancyZonesData.cs b/src/modules/fancyzones/FancyZonesCLI/FancyZonesData.cs new file mode 100644 index 0000000000..2396c51f44 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesCLI/FancyZonesData.cs @@ -0,0 +1,142 @@ +// 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; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; + +namespace FancyZonesCLI; + +/// +/// Provides methods to read and write FancyZones configuration data. +/// +internal static class FancyZonesData +{ + /// + /// Try to read applied layouts configuration. + /// + public static bool TryReadAppliedLayouts(out AppliedLayouts result, out string error) + { + return TryReadJsonFile(FancyZonesPaths.AppliedLayouts, FancyZonesJsonContext.Default.AppliedLayouts, out result, out error); + } + + /// + /// Read applied layouts or return null if not found. + /// + public static AppliedLayouts ReadAppliedLayouts() + { + return ReadJsonFileOrDefault(FancyZonesPaths.AppliedLayouts, FancyZonesJsonContext.Default.AppliedLayouts); + } + + /// + /// Write applied layouts configuration. + /// + public static void WriteAppliedLayouts(AppliedLayouts layouts) + { + WriteJsonFile(FancyZonesPaths.AppliedLayouts, layouts, FancyZonesJsonContext.Default.AppliedLayouts); + } + + /// + /// Read custom layouts or return null if not found. + /// + public static CustomLayouts ReadCustomLayouts() + { + return ReadJsonFileOrDefault(FancyZonesPaths.CustomLayouts, FancyZonesJsonContext.Default.CustomLayouts); + } + + /// + /// Read layout templates or return null if not found. + /// + public static LayoutTemplates ReadLayoutTemplates() + { + return ReadJsonFileOrDefault(FancyZonesPaths.LayoutTemplates, FancyZonesJsonContext.Default.LayoutTemplates); + } + + /// + /// Read layout hotkeys or return null if not found. + /// + public static LayoutHotkeys ReadLayoutHotkeys() + { + return ReadJsonFileOrDefault(FancyZonesPaths.LayoutHotkeys, FancyZonesJsonContext.Default.LayoutHotkeys); + } + + /// + /// Write layout hotkeys configuration. + /// + public static void WriteLayoutHotkeys(LayoutHotkeys hotkeys) + { + WriteJsonFile(FancyZonesPaths.LayoutHotkeys, hotkeys, FancyZonesJsonContext.Default.LayoutHotkeys); + } + + /// + /// Check if editor parameters file exists. + /// + public static bool EditorParametersExist() + { + return File.Exists(FancyZonesPaths.EditorParameters); + } + + private static bool TryReadJsonFile(string filePath, JsonTypeInfo jsonTypeInfo, out T result, out string error) + where T : class + { + result = null; + error = null; + + Logger.LogDebug($"Reading file: {filePath}"); + + if (!File.Exists(filePath)) + { + error = $"File not found: {Path.GetFileName(filePath)}"; + Logger.LogWarning(error); + return false; + } + + try + { + var json = File.ReadAllText(filePath); + result = JsonSerializer.Deserialize(json, jsonTypeInfo); + if (result == null) + { + error = $"Failed to parse {Path.GetFileName(filePath)}"; + Logger.LogError(error); + return false; + } + + Logger.LogDebug($"Successfully read {Path.GetFileName(filePath)}"); + return true; + } + catch (JsonException ex) + { + error = $"JSON parse error in {Path.GetFileName(filePath)}: {ex.Message}"; + Logger.LogError(error, ex); + return false; + } + catch (IOException ex) + { + error = $"Failed to read {Path.GetFileName(filePath)}: {ex.Message}"; + Logger.LogError(error, ex); + return false; + } + } + + private static T ReadJsonFileOrDefault(string filePath, JsonTypeInfo jsonTypeInfo, T defaultValue = null) + where T : class + { + if (TryReadJsonFile(filePath, jsonTypeInfo, out var result, out _)) + { + return result; + } + + return defaultValue; + } + + private static void WriteJsonFile(string filePath, T data, JsonTypeInfo jsonTypeInfo) + { + Logger.LogDebug($"Writing file: {filePath}"); + var json = JsonSerializer.Serialize(data, jsonTypeInfo); + File.WriteAllText(filePath, json); + Logger.LogInfo($"Successfully wrote {Path.GetFileName(filePath)}"); + } +} diff --git a/src/modules/fancyzones/FancyZonesCLI/FancyZonesPaths.cs b/src/modules/fancyzones/FancyZonesCLI/FancyZonesPaths.cs new file mode 100644 index 0000000000..f04d375392 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesCLI/FancyZonesPaths.cs @@ -0,0 +1,30 @@ +// 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; + +namespace FancyZonesCLI; + +/// +/// Provides paths to FancyZones configuration files. +/// +internal static class FancyZonesPaths +{ + private static readonly string DataPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "Microsoft", + "PowerToys", + "FancyZones"); + + public static string AppliedLayouts => Path.Combine(DataPath, "applied-layouts.json"); + + public static string CustomLayouts => Path.Combine(DataPath, "custom-layouts.json"); + + public static string LayoutTemplates => Path.Combine(DataPath, "layout-templates.json"); + + public static string LayoutHotkeys => Path.Combine(DataPath, "layout-hotkeys.json"); + + public static string EditorParameters => Path.Combine(DataPath, "editor-parameters.json"); +} diff --git a/src/modules/fancyzones/FancyZonesCLI/LayoutVisualizer.cs b/src/modules/fancyzones/FancyZonesCLI/LayoutVisualizer.cs new file mode 100644 index 0000000000..fecdf33dbe --- /dev/null +++ b/src/modules/fancyzones/FancyZonesCLI/LayoutVisualizer.cs @@ -0,0 +1,550 @@ +// 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; +using System.Text.Json; + +namespace FancyZonesCLI; + +public static class LayoutVisualizer +{ + public static string DrawTemplateLayout(TemplateLayout template) + { + var sb = new StringBuilder(); + sb.AppendLine(" Visual Preview:"); + + switch (template.Type.ToLowerInvariant()) + { + case "focus": + sb.Append(RenderFocusLayout(template.ZoneCount > 0 ? template.ZoneCount : 3)); + break; + case "columns": + sb.Append(RenderGridLayout(1, template.ZoneCount > 0 ? template.ZoneCount : 3)); + break; + case "rows": + sb.Append(RenderGridLayout(template.ZoneCount > 0 ? template.ZoneCount : 3, 1)); + break; + case "grid": + // Grid layout: calculate rows and columns from zone count + // Algorithm from GridLayoutModel.InitGrid() - tries to make it close to square + // with cols >= rows preference + int zoneCount = template.ZoneCount > 0 ? template.ZoneCount : 3; + int rows = 1; + while (zoneCount / rows >= rows) + { + rows++; + } + + rows--; + int cols = zoneCount / rows; + if (zoneCount % rows != 0) + { + cols++; + } + + sb.Append(RenderGridLayoutWithZoneCount(rows, cols, zoneCount)); + break; + case "priority-grid": + sb.Append(RenderPriorityGridLayout(template.ZoneCount > 0 ? template.ZoneCount : 3)); + break; + case "blank": + sb.AppendLine(" (No zones)"); + break; + default: + sb.AppendLine(CultureInfo.InvariantCulture, $" ({template.Type} layout)"); + break; + } + + return sb.ToString(); + } + + public static string DrawCustomLayout(CustomLayout layout) + { + if (layout.Info.ValueKind == JsonValueKind.Undefined || layout.Info.ValueKind == JsonValueKind.Null) + { + return string.Empty; + } + + var sb = new StringBuilder(); + sb.AppendLine(" Visual Preview:"); + + if (layout.Type == "grid" && + layout.Info.TryGetProperty("rows", out var rows) && + layout.Info.TryGetProperty("columns", out var cols)) + { + int r = rows.GetInt32(); + int c = cols.GetInt32(); + + // Check if there's a cell-child-map (merged cells) + if (layout.Info.TryGetProperty("cell-child-map", out var cellMap)) + { + sb.Append(RenderGridLayoutWithMergedCells(r, c, cellMap)); + } + else + { + int height = r >= 4 ? 12 : 8; + sb.Append(RenderGridLayout(r, c, 30, height)); + } + } + else if (layout.Type == "canvas" && + layout.Info.TryGetProperty("zones", out var zones) && + layout.Info.TryGetProperty("ref-width", out var refWidth) && + layout.Info.TryGetProperty("ref-height", out var refHeight)) + { + sb.Append(RenderCanvasLayout(zones, refWidth.GetInt32(), refHeight.GetInt32())); + } + + return sb.ToString(); + } + + private static string RenderFocusLayout(int zoneCount = 3) + { + var sb = new StringBuilder(); + + // Focus layout: overlapping zones with cascading offset + if (zoneCount == 1) + { + sb.AppendLine(" +-------+"); + sb.AppendLine(" | |"); + sb.AppendLine(" | |"); + sb.AppendLine(" +-------+"); + } + else if (zoneCount == 2) + { + sb.AppendLine(" +-------+"); + sb.AppendLine(" | |"); + sb.AppendLine(" | +-------+"); + sb.AppendLine(" +-| |"); + sb.AppendLine(" | |"); + sb.AppendLine(" +-------+"); + } + else + { + sb.AppendLine(" +-------+"); + sb.AppendLine(" | |"); + sb.AppendLine(" | +-------+"); + sb.AppendLine(" +-| |"); + sb.AppendLine(" | +-------+"); + sb.AppendLine(" +-| |"); + sb.AppendLine(" ..."); + sb.AppendLine(CultureInfo.InvariantCulture, $" (total: {zoneCount} zones)"); + sb.AppendLine(" ..."); + sb.AppendLine(" | +-------+"); + sb.AppendLine(" +-| |"); + sb.AppendLine(" | |"); + sb.AppendLine(" +-------+"); + } + + return sb.ToString(); + } + + private static string RenderPriorityGridLayout(int zoneCount = 3) + { + // Priority Grid has predefined layouts for zone counts 1-11 + // Data format from GridLayoutModel._priorityData + if (zoneCount >= 1 && zoneCount <= 11) + { + int[,] cellMap = GetPriorityGridCellMap(zoneCount); + return RenderGridLayoutWithCellMap(cellMap); + } + else + { + // > 11 zones: use grid layout + int rows = 1; + while (zoneCount / rows >= rows) + { + rows++; + } + + rows--; + int cols = zoneCount / rows; + if (zoneCount % rows != 0) + { + cols++; + } + + return RenderGridLayoutWithZoneCount(rows, cols, zoneCount); + } + } + + private static int[,] GetPriorityGridCellMap(int zoneCount) + { + // Parsed from Editor's _priorityData byte arrays + return zoneCount switch + { + 1 => new int[,] { { 0 } }, + 2 => new int[,] { { 0, 1 } }, + 3 => new int[,] { { 0, 1, 2 } }, + 4 => new int[,] { { 0, 1, 2 }, { 0, 1, 3 } }, + 5 => new int[,] { { 0, 1, 2 }, { 3, 1, 4 } }, + 6 => new int[,] { { 0, 1, 2 }, { 0, 1, 3 }, { 4, 1, 5 } }, + 7 => new int[,] { { 0, 1, 2 }, { 3, 1, 4 }, { 5, 1, 6 } }, + 8 => new int[,] { { 0, 1, 2, 3 }, { 4, 1, 2, 5 }, { 6, 1, 2, 7 } }, + 9 => new int[,] { { 0, 1, 2, 3 }, { 4, 1, 2, 5 }, { 6, 1, 7, 8 } }, + 10 => new int[,] { { 0, 1, 2, 3 }, { 4, 1, 5, 6 }, { 7, 1, 8, 9 } }, + 11 => new int[,] { { 0, 1, 2, 3 }, { 4, 1, 5, 6 }, { 7, 8, 9, 10 } }, + _ => new int[,] { { 0 } }, + }; + } + + private static string RenderGridLayoutWithCellMap(int[,] cellMap, int width = 30, int height = 8) + { + var sb = new StringBuilder(); + int rows = cellMap.GetLength(0); + int cols = cellMap.GetLength(1); + + int cellWidth = width / cols; + int cellHeight = height / rows; + + for (int r = 0; r < rows; r++) + { + // Top border + sb.Append(" +"); + for (int c = 0; c < cols; c++) + { + bool mergeTop = r > 0 && cellMap[r, c] == cellMap[r - 1, c]; + bool mergeLeft = c > 0 && cellMap[r, c] == cellMap[r, c - 1]; + + if (mergeTop) + { + sb.Append(mergeLeft ? new string(' ', cellWidth) : new string(' ', cellWidth - 1) + "+"); + } + else + { + sb.Append(mergeLeft ? new string('-', cellWidth) : new string('-', cellWidth - 1) + "+"); + } + } + + sb.AppendLine(); + + // Cell content + for (int h = 0; h < cellHeight - 1; h++) + { + sb.Append(" "); + for (int c = 0; c < cols; c++) + { + bool mergeLeft = c > 0 && cellMap[r, c] == cellMap[r, c - 1]; + sb.Append(mergeLeft ? ' ' : '|'); + sb.Append(' ', cellWidth - 1); + } + + sb.AppendLine("|"); + } + } + + // Bottom border + sb.Append(" +"); + for (int c = 0; c < cols; c++) + { + sb.Append('-', cellWidth - 1); + sb.Append('+'); + } + + sb.AppendLine(); + return sb.ToString(); + } + + private static string RenderGridLayoutWithMergedCells(int rows, int cols, JsonElement cellMap) + { + var sb = new StringBuilder(); + const int displayWidth = 39; + const int displayHeight = 12; + + // Build zone map from cell-child-map + int[,] zoneMap = new int[rows, cols]; + for (int r = 0; r < rows; r++) + { + var rowArray = cellMap[r]; + for (int c = 0; c < cols; c++) + { + zoneMap[r, c] = rowArray[c].GetInt32(); + } + } + + int cellHeight = displayHeight / rows; + int cellWidth = displayWidth / cols; + + // Draw top border + sb.Append(" +"); + sb.Append('-', displayWidth); + sb.AppendLine("+"); + + // Draw rows + for (int r = 0; r < rows; r++) + { + for (int h = 0; h < cellHeight; h++) + { + sb.Append(" |"); + + for (int c = 0; c < cols; c++) + { + int currentZone = zoneMap[r, c]; + int leftZone = c > 0 ? zoneMap[r, c - 1] : -1; + bool needLeftBorder = c > 0 && currentZone != leftZone; + + bool zoneHasTopBorder = r > 0 && h == 0 && currentZone != zoneMap[r - 1, c]; + + if (needLeftBorder) + { + sb.Append('|'); + sb.Append(zoneHasTopBorder ? '-' : ' ', cellWidth - 1); + } + else + { + sb.Append(zoneHasTopBorder ? '-' : ' ', cellWidth); + } + } + + sb.AppendLine("|"); + } + } + + // Draw bottom border + sb.Append(" +"); + sb.Append('-', displayWidth); + sb.AppendLine("+"); + + return sb.ToString(); + } + + public static string RenderGridLayoutWithZoneCount(int rows, int cols, int zoneCount, int width = 30, int height = 8) + { + var sb = new StringBuilder(); + + // Build zone map like Editor's InitGrid + int[,] zoneMap = new int[rows, cols]; + int index = 0; + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + zoneMap[r, c] = index++; + if (index == zoneCount) + { + index--; // Remaining cells use the last zone index + } + } + } + + int cellWidth = width / cols; + int cellHeight = height / rows; + + for (int r = 0; r < rows; r++) + { + // Top border + sb.Append(" +"); + for (int c = 0; c < cols; c++) + { + bool mergeLeft = c > 0 && zoneMap[r, c] == zoneMap[r, c - 1]; + sb.Append('-', mergeLeft ? cellWidth : cellWidth - 1); + if (!mergeLeft) + { + sb.Append('+'); + } + } + + sb.AppendLine(); + + // Cell content + for (int h = 0; h < cellHeight - 1; h++) + { + sb.Append(" "); + for (int c = 0; c < cols; c++) + { + bool mergeLeft = c > 0 && zoneMap[r, c] == zoneMap[r, c - 1]; + sb.Append(mergeLeft ? ' ' : '|'); + sb.Append(' ', cellWidth - 1); + } + + sb.AppendLine("|"); + } + } + + // Bottom border + sb.Append(" +"); + for (int c = 0; c < cols; c++) + { + sb.Append('-', cellWidth - 1); + sb.Append('+'); + } + + sb.AppendLine(); + return sb.ToString(); + } + + public static string RenderGridLayout(int rows, int cols, int width = 30, int height = 8) + { + var sb = new StringBuilder(); + int cellWidth = width / cols; + int cellHeight = height / rows; + + for (int r = 0; r < rows; r++) + { + // Top border + sb.Append(" +"); + for (int c = 0; c < cols; c++) + { + sb.Append('-', cellWidth - 1); + sb.Append('+'); + } + + sb.AppendLine(); + + // Cell content + for (int h = 0; h < cellHeight - 1; h++) + { + sb.Append(" "); + for (int c = 0; c < cols; c++) + { + sb.Append('|'); + sb.Append(' ', cellWidth - 1); + } + + sb.AppendLine("|"); + } + } + + // Bottom border + sb.Append(" +"); + for (int c = 0; c < cols; c++) + { + sb.Append('-', cellWidth - 1); + sb.Append('+'); + } + + sb.AppendLine(); + return sb.ToString(); + } + + private static string RenderCanvasLayout(JsonElement zones, int refWidth, int refHeight) + { + var sb = new StringBuilder(); + const int displayWidth = 49; + const int displayHeight = 15; + + // Create a 2D array to track which zones occupy each position + var zoneGrid = new List[displayHeight, displayWidth]; + for (int i = 0; i < displayHeight; i++) + { + for (int j = 0; j < displayWidth; j++) + { + zoneGrid[i, j] = new List(); + } + } + + // Map each zone to the grid + int zoneId = 0; + var zoneList = new List<(int X, int Y, int Width, int Height, int Id)>(); + + foreach (var zone in zones.EnumerateArray()) + { + int x = zone.GetProperty("X").GetInt32(); + int y = zone.GetProperty("Y").GetInt32(); + int w = zone.GetProperty("width").GetInt32(); + int h = zone.GetProperty("height").GetInt32(); + + int dx = Math.Max(0, Math.Min(displayWidth - 1, x * displayWidth / refWidth)); + int dy = Math.Max(0, Math.Min(displayHeight - 1, y * displayHeight / refHeight)); + int dw = Math.Max(3, w * displayWidth / refWidth); + int dh = Math.Max(2, h * displayHeight / refHeight); + + if (dx + dw > displayWidth) + { + dw = displayWidth - dx; + } + + if (dy + dh > displayHeight) + { + dh = displayHeight - dy; + } + + zoneList.Add((dx, dy, dw, dh, zoneId)); + + for (int r = dy; r < dy + dh && r < displayHeight; r++) + { + for (int c = dx; c < dx + dw && c < displayWidth; c++) + { + zoneGrid[r, c].Add(zoneId); + } + } + + zoneId++; + } + + // Draw top border + sb.Append(" +"); + sb.Append('-', displayWidth); + sb.AppendLine("+"); + + // Draw each row + char[] shades = { '.', ':', '░', '▒', '▓', '█', '◆', '●', '■', '▪' }; + + for (int r = 0; r < displayHeight; r++) + { + sb.Append(" |"); + for (int c = 0; c < displayWidth; c++) + { + var zonesHere = zoneGrid[r, c]; + + if (zonesHere.Count == 0) + { + sb.Append(' '); + } + else + { + int topZone = zonesHere[zonesHere.Count - 1]; + var rect = zoneList[topZone]; + + bool isTopEdge = r == rect.Y; + bool isBottomEdge = r == rect.Y + rect.Height - 1; + bool isLeftEdge = c == rect.X; + bool isRightEdge = c == rect.X + rect.Width - 1; + + if ((isTopEdge || isBottomEdge) && (isLeftEdge || isRightEdge)) + { + sb.Append('+'); + } + else if (isTopEdge || isBottomEdge) + { + sb.Append('-'); + } + else if (isLeftEdge || isRightEdge) + { + sb.Append('|'); + } + else + { + sb.Append(shades[topZone % shades.Length]); + } + } + } + + sb.AppendLine("|"); + } + + // Draw bottom border + sb.Append(" +"); + sb.Append('-', displayWidth); + sb.AppendLine("+"); + + // Draw legend + sb.AppendLine(); + sb.Append(" Legend: "); + for (int i = 0; i < Math.Min(zoneId, shades.Length); i++) + { + if (i > 0) + { + sb.Append(", "); + } + + sb.Append(CultureInfo.InvariantCulture, $"Zone {i} = {shades[i]}"); + } + + sb.AppendLine(); + return sb.ToString(); + } +} diff --git a/src/modules/fancyzones/FancyZonesCLI/Logger.cs b/src/modules/fancyzones/FancyZonesCLI/Logger.cs new file mode 100644 index 0000000000..3f62abf7eb --- /dev/null +++ b/src/modules/fancyzones/FancyZonesCLI/Logger.cs @@ -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.IO; +using System.Runtime.CompilerServices; + +namespace FancyZonesCLI; + +/// +/// Simple logger for FancyZones CLI. +/// Logs to %LOCALAPPDATA%\Microsoft\PowerToys\FancyZones\CLI\Logs +/// +internal static class Logger +{ + private static readonly object LockObj = new(); + private static string _logFilePath = string.Empty; + private static bool _isInitialized; + + /// + /// Gets the path to the current log file. + /// + public static string LogFilePath => _logFilePath; + + /// + /// Initializes the logger. + /// + public static void InitializeLogger() + { + if (_isInitialized) + { + return; + } + + try + { + var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + var logDirectory = Path.Combine(localAppData, "Microsoft", "PowerToys", "FancyZones", "CLI", "Logs"); + + if (!Directory.Exists(logDirectory)) + { + Directory.CreateDirectory(logDirectory); + } + + var logFileName = $"FancyZonesCLI_{DateTime.Now:yyyy-MM-dd}.log"; + _logFilePath = Path.Combine(logDirectory, logFileName); + _isInitialized = true; + + LogInfo("FancyZones CLI started"); + } + catch + { + // Silently fail if logging cannot be initialized + } + } + + /// + /// Logs an error message. + /// + public static void LogError(string message, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) + { + Log("ERROR", message, memberName, sourceFilePath, sourceLineNumber); + } + + /// + /// Logs an error message with exception details. + /// + public static void LogError(string message, Exception ex, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) + { + var fullMessage = ex == null + ? message + : $"{message} | Exception: {ex.GetType().Name}: {ex.Message}"; + Log("ERROR", fullMessage, memberName, sourceFilePath, sourceLineNumber); + } + + /// + /// Logs a warning message. + /// + public static void LogWarning(string message, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) + { + Log("WARN", message, memberName, sourceFilePath, sourceLineNumber); + } + + /// + /// Logs an informational message. + /// + public static void LogInfo(string message, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) + { + Log("INFO", message, memberName, sourceFilePath, sourceLineNumber); + } + + /// + /// Logs a debug message (only in DEBUG builds). + /// + [System.Diagnostics.Conditional("DEBUG")] + public static void LogDebug(string message, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) + { + Log("DEBUG", message, memberName, sourceFilePath, sourceLineNumber); + } + + private static void Log(string level, string message, string memberName, string sourceFilePath, int sourceLineNumber) + { + if (!_isInitialized || string.IsNullOrEmpty(_logFilePath)) + { + return; + } + + try + { + var fileName = Path.GetFileName(sourceFilePath); + var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); + var logEntry = $"[{timestamp}] [{level}] [{fileName}:{sourceLineNumber}] [{memberName}] {message}{Environment.NewLine}"; + + lock (LockObj) + { + File.AppendAllText(_logFilePath, logEntry); + } + } + catch + { + // Silently fail if logging fails + } + } +} diff --git a/src/modules/fancyzones/FancyZonesCLI/Models.cs b/src/modules/fancyzones/FancyZonesCLI/Models.cs new file mode 100644 index 0000000000..0c8bbefe54 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesCLI/Models.cs @@ -0,0 +1,137 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace FancyZonesCLI; + +// JSON Source Generator for AOT compatibility +[JsonSerializable(typeof(LayoutTemplates))] +[JsonSerializable(typeof(CustomLayouts))] +[JsonSerializable(typeof(AppliedLayouts))] +[JsonSerializable(typeof(LayoutHotkeys))] +[JsonSourceGenerationOptions(WriteIndented = true)] +internal partial class FancyZonesJsonContext : JsonSerializerContext +{ +} + +// Layout Templates +public sealed class LayoutTemplates +{ + [JsonPropertyName("layout-templates")] + public List Templates { get; set; } +} + +public sealed class TemplateLayout +{ + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; + + [JsonPropertyName("zone-count")] + public int ZoneCount { get; set; } + + [JsonPropertyName("show-spacing")] + public bool ShowSpacing { get; set; } + + [JsonPropertyName("spacing")] + public int Spacing { get; set; } + + [JsonPropertyName("sensitivity-radius")] + public int SensitivityRadius { get; set; } +} + +// Custom Layouts +public sealed class CustomLayouts +{ + [JsonPropertyName("custom-layouts")] + public List Layouts { get; set; } +} + +public sealed class CustomLayout +{ + [JsonPropertyName("uuid")] + public string Uuid { get; set; } = string.Empty; + + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; + + [JsonPropertyName("info")] + public JsonElement Info { get; set; } +} + +// Applied Layouts +public sealed class AppliedLayouts +{ + [JsonPropertyName("applied-layouts")] + public List Layouts { get; set; } +} + +public sealed class AppliedLayoutWrapper +{ + [JsonPropertyName("device")] + public DeviceInfo Device { get; set; } = new(); + + [JsonPropertyName("applied-layout")] + public AppliedLayoutInfo AppliedLayout { get; set; } = new(); +} + +public sealed class DeviceInfo +{ + [JsonPropertyName("monitor")] + public string Monitor { get; set; } = string.Empty; + + [JsonPropertyName("monitor-instance")] + public string MonitorInstance { get; set; } = string.Empty; + + [JsonPropertyName("monitor-number")] + public int MonitorNumber { get; set; } + + [JsonPropertyName("serial-number")] + public string SerialNumber { get; set; } = string.Empty; + + [JsonPropertyName("virtual-desktop")] + public string VirtualDesktop { get; set; } = string.Empty; +} + +public sealed class AppliedLayoutInfo +{ + [JsonPropertyName("uuid")] + public string Uuid { get; set; } = string.Empty; + + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; + + [JsonPropertyName("show-spacing")] + public bool ShowSpacing { get; set; } + + [JsonPropertyName("spacing")] + public int Spacing { get; set; } + + [JsonPropertyName("zone-count")] + public int ZoneCount { get; set; } + + [JsonPropertyName("sensitivity-radius")] + public int SensitivityRadius { get; set; } +} + +// Layout Hotkeys +public sealed class LayoutHotkeys +{ + [JsonPropertyName("layout-hotkeys")] + public List Hotkeys { get; set; } +} + +public sealed class LayoutHotkey +{ + [JsonPropertyName("key")] + public int Key { get; set; } + + [JsonPropertyName("layout-id")] + public string LayoutId { get; set; } = string.Empty; +} diff --git a/src/modules/fancyzones/FancyZonesCLI/NativeMethods.cs b/src/modules/fancyzones/FancyZonesCLI/NativeMethods.cs new file mode 100644 index 0000000000..efab0859bd --- /dev/null +++ b/src/modules/fancyzones/FancyZonesCLI/NativeMethods.cs @@ -0,0 +1,56 @@ +// 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 Windows.Win32; +using Windows.Win32.Foundation; + +namespace FancyZonesCLI; + +/// +/// Native Windows API methods for FancyZones CLI. +/// +internal static class NativeMethods +{ + // Registered Windows messages for notifying FancyZones + private static uint wmPrivAppliedLayoutsFileUpdate; + private static uint wmPrivLayoutHotkeysFileUpdate; + + /// + /// Gets the Windows message ID for applied layouts file update notification. + /// + public static uint WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE => wmPrivAppliedLayoutsFileUpdate; + + /// + /// Gets the Windows message ID for layout hotkeys file update notification. + /// + public static uint WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE => wmPrivLayoutHotkeysFileUpdate; + + /// + /// Initializes the Windows messages used for FancyZones notifications. + /// + public static void InitializeWindowMessages() + { + wmPrivAppliedLayoutsFileUpdate = PInvoke.RegisterWindowMessage("{2ef2c8a7-e0d5-4f31-9ede-52aade2d284d}"); + wmPrivLayoutHotkeysFileUpdate = PInvoke.RegisterWindowMessage("{07229b7e-4f22-4357-b136-33c289be2295}"); + } + + /// + /// Broadcasts a notification message to FancyZones. + /// + /// The Windows message ID to broadcast. + public static void NotifyFancyZones(uint message) + { + PInvoke.PostMessage(HWND.HWND_BROADCAST, message, 0, 0); + } + + /// + /// Brings the specified window to the foreground. + /// + /// A handle to the window. + /// True if the window was brought to the foreground. + public static bool SetForegroundWindow(nint hWnd) + { + return PInvoke.SetForegroundWindow(new HWND(hWnd)); + } +} diff --git a/src/modules/fancyzones/FancyZonesCLI/NativeMethods.json b/src/modules/fancyzones/FancyZonesCLI/NativeMethods.json new file mode 100644 index 0000000000..89cee38a92 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesCLI/NativeMethods.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://aka.ms/CsWin32.schema.json", + "emitSingleFile": true, + "allowMarshaling": false +} diff --git a/src/modules/fancyzones/FancyZonesCLI/NativeMethods.txt b/src/modules/fancyzones/FancyZonesCLI/NativeMethods.txt new file mode 100644 index 0000000000..e3555c2333 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesCLI/NativeMethods.txt @@ -0,0 +1,4 @@ +PostMessage +SetForegroundWindow +RegisterWindowMessage +HWND_BROADCAST diff --git a/src/modules/fancyzones/FancyZonesCLI/Program.cs b/src/modules/fancyzones/FancyZonesCLI/Program.cs new file mode 100644 index 0000000000..1b133dfa36 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesCLI/Program.cs @@ -0,0 +1,115 @@ +// 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.Linq; +using FancyZonesCLI.Commands; + +namespace FancyZonesCLI; + +internal sealed class Program +{ + private static int Main(string[] args) + { + // Initialize logger + Logger.InitializeLogger(); + Logger.LogInfo($"CLI invoked with args: [{string.Join(", ", args)}]"); + + // Initialize Windows messages + NativeMethods.InitializeWindowMessages(); + + (int ExitCode, string Output) result; + + if (args.Length == 0) + { + result = (1, GetUsageText()); + } + else + { + var command = args[0].ToLowerInvariant(); + + result = command switch + { + "open-editor" or "editor" or "e" => EditorCommands.OpenEditor(), + "get-monitors" or "monitors" or "m" => MonitorCommands.GetMonitors(), + "get-layouts" or "layouts" or "ls" => LayoutCommands.GetLayouts(), + "get-active-layout" or "active" or "get-active" or "a" => LayoutCommands.GetActiveLayout(), + "set-layout" or "set" or "s" => args.Length >= 2 + ? LayoutCommands.SetLayout(args.Skip(1).ToArray(), NativeMethods.NotifyFancyZones, NativeMethods.WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE) + : (1, "Error: set-layout requires a UUID parameter"), + "open-settings" or "settings" => EditorCommands.OpenSettings(), + "get-hotkeys" or "hotkeys" or "hk" => HotkeyCommands.GetHotkeys(), + "set-hotkey" or "shk" => args.Length >= 3 + ? HotkeyCommands.SetHotkey(int.Parse(args[1], CultureInfo.InvariantCulture), args[2], NativeMethods.NotifyFancyZones, NativeMethods.WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE) + : (1, "Error: set-hotkey requires "), + "remove-hotkey" or "rhk" => args.Length >= 2 + ? HotkeyCommands.RemoveHotkey(int.Parse(args[1], CultureInfo.InvariantCulture), NativeMethods.NotifyFancyZones, NativeMethods.WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE) + : (1, "Error: remove-hotkey requires "), + "help" or "--help" or "-h" => (0, GetUsageText()), + _ => (1, $"Error: Unknown command: {command}\n\n{GetUsageText()}"), + }; + } + + // Log result + if (result.ExitCode == 0) + { + Logger.LogInfo($"Command completed successfully"); + } + else + { + Logger.LogWarning($"Command failed with exit code {result.ExitCode}: {result.Output}"); + } + + // Output result + if (!string.IsNullOrEmpty(result.Output)) + { + Console.WriteLine(result.Output); + } + + return result.ExitCode; + } + + private static string GetUsageText() + { + return """ + FancyZones CLI - Command line interface for FancyZones + ====================================================== + + Usage: FancyZonesCLI.exe [options] + + Commands: + open-editor (editor, e) Launch FancyZones layout editor + get-monitors (monitors, m) List all monitors and their properties + get-layouts (layouts, ls) List all available layouts + get-active-layout (get-active, active, a) + Show currently active layout + set-layout (set, s) [options] + Set layout by UUID + --monitor Apply to monitor N (1-based) + --all Apply to all monitors + open-settings (settings) Open FancyZones settings page + get-hotkeys (hotkeys, hk) List all layout hotkeys + set-hotkey (shk) Assign hotkey (0-9) to CUSTOM layout + Note: Only custom layouts work with hotkeys + remove-hotkey (rhk) Remove hotkey assignment + help Show this help message + + + Examples: + FancyZonesCLI.exe e # Open editor (short) + FancyZonesCLI.exe m # List monitors (short) + FancyZonesCLI.exe ls # List layouts (short) + FancyZonesCLI.exe a # Get active layout (short) + FancyZonesCLI.exe s focus --all # Set layout (short) + FancyZonesCLI.exe open-editor # Open editor (long) + FancyZonesCLI.exe get-monitors + FancyZonesCLI.exe get-layouts + FancyZonesCLI.exe set-layout {12345678-1234-1234-1234-123456789012} + FancyZonesCLI.exe set-layout focus --monitor 2 + FancyZonesCLI.exe set-layout columns --all + FancyZonesCLI.exe set-hotkey 3 {12345678-1234-1234-1234-123456789012} + """; + } +} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.PowerToys/Components/UtilityProvider.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.PowerToys/Components/UtilityProvider.cs index a953496500..74d35a5b18 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.PowerToys/Components/UtilityProvider.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.PowerToys/Components/UtilityProvider.cs @@ -27,7 +27,7 @@ namespace Microsoft.PowerToys.Run.Plugin.PowerToys public UtilityProvider() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; var generalSettings = settingsUtils.GetSettings(); _utilities = new List(); @@ -228,7 +228,7 @@ namespace Microsoft.PowerToys.Run.Plugin.PowerToys { retryCount++; - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; var generalSettings = settingsUtils.GetSettings(); foreach (var u in _utilities) diff --git a/src/modules/launcher/PowerLauncher/SettingsReader.cs b/src/modules/launcher/PowerLauncher/SettingsReader.cs index 41d0524f69..dbba7e7906 100644 --- a/src/modules/launcher/PowerLauncher/SettingsReader.cs +++ b/src/modules/launcher/PowerLauncher/SettingsReader.cs @@ -38,7 +38,7 @@ namespace PowerLauncher public SettingsReader(PowerToysRunSettings settings, ThemeManager themeManager) { - _settingsUtils = new SettingsUtils(); + _settingsUtils = SettingsUtils.Default; _settings = settings; _themeManager = themeManager; diff --git a/src/modules/peek/Peek.FilePreviewer/Models/PreviewSettings.cs b/src/modules/peek/Peek.FilePreviewer/Models/PreviewSettings.cs index 3df7fc23a1..1ba5e676a5 100644 --- a/src/modules/peek/Peek.FilePreviewer/Models/PreviewSettings.cs +++ b/src/modules/peek/Peek.FilePreviewer/Models/PreviewSettings.cs @@ -34,7 +34,7 @@ namespace Peek.FilePreviewer.Models public PreviewSettings() { - _settingsUtils = new SettingsUtils(); + _settingsUtils = SettingsUtils.Default; SourceCodeWrapText = false; SourceCodeTryFormat = false; SourceCodeFontSize = 14; diff --git a/src/modules/peek/Peek.UI/Services/UserSettings.cs b/src/modules/peek/Peek.UI/Services/UserSettings.cs index 77257eaf80..e3750fe8e1 100644 --- a/src/modules/peek/Peek.UI/Services/UserSettings.cs +++ b/src/modules/peek/Peek.UI/Services/UserSettings.cs @@ -77,7 +77,7 @@ namespace Peek.UI public UserSettings() { - _settingsUtils = new SettingsUtils(); + _settingsUtils = SettingsUtils.Default; LoadSettingsFromJson(); diff --git a/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs b/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs index 1a1657ecaf..81e3d5c56f 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs @@ -24,7 +24,7 @@ public class SettingsService public SettingsService(KeyboardListener keyboardListener) { - _settingsUtils = new SettingsUtils(); + _settingsUtils = SettingsUtils.Default; _keyboardListener = keyboardListener; ReadSettings(); _watcher = Helper.GetFileWatcher(PowerAccentModuleName, "settings.json", () => { ReadSettings(); }); diff --git a/src/modules/poweraccent/PowerAccent.Core/Tools/CharactersUsageInfo.cs b/src/modules/poweraccent/PowerAccent.Core/Tools/CharactersUsageInfo.cs index 492a9828d9..ef2fa1db7e 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Tools/CharactersUsageInfo.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Tools/CharactersUsageInfo.cs @@ -19,7 +19,7 @@ namespace PowerAccent.Core.Tools public CharactersUsageInfo() { - _filePath = new SettingsUtils().GetSettingsFilePath(PowerAccentSettings.ModuleName, "UsageInfo.json"); + _filePath = SettingsUtils.Default.GetSettingsFilePath(PowerAccentSettings.ModuleName, "UsageInfo.json"); var data = GetUsageInfoData(); _characterUsageCounters = data.CharacterUsageCounters; _characterUsageTimestamp = data.CharacterUsageTimestamp; diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj index 7f19db44b8..de71eb2188 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj @@ -36,6 +36,7 @@ PowerToys.PowerRename.pri win10-x64;win10-arm64 + false diff --git a/src/modules/previewpane/MonacoPreviewHandler/Settings.cs b/src/modules/previewpane/MonacoPreviewHandler/Settings.cs index 94eeab308f..80ee876104 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/Settings.cs +++ b/src/modules/previewpane/MonacoPreviewHandler/Settings.cs @@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco /// public class Settings { - private static SettingsUtils moduleSettings = new SettingsUtils(); + private static SettingsUtils moduleSettings = SettingsUtils.Default; /// /// Gets a value indicating whether word wrapping should be applied. Set by PT settings. diff --git a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs index 2301c7beb0..d6dbd74251 100644 --- a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs +++ b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs @@ -155,7 +155,7 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Stl { try { - var moduleSettings = new SettingsUtils(); + var moduleSettings = SettingsUtils.Default; var colorString = moduleSettings.GetSettings(PowerPreviewSettings.ModuleName).Properties.StlThumbnailColor.Value; diff --git a/src/modules/previewpane/SvgPreviewHandler/Settings.cs b/src/modules/previewpane/SvgPreviewHandler/Settings.cs index 925da8307a..dfde5bed03 100644 --- a/src/modules/previewpane/SvgPreviewHandler/Settings.cs +++ b/src/modules/previewpane/SvgPreviewHandler/Settings.cs @@ -8,7 +8,7 @@ namespace SvgPreviewHandler { internal sealed class Settings { - private static readonly SettingsUtils ModuleSettings = new SettingsUtils(); + private static readonly SettingsUtils ModuleSettings = SettingsUtils.Default; public int ColorMode { diff --git a/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs b/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs index 10ebf74314..08708009d0 100644 --- a/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs +++ b/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs @@ -592,7 +592,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library /// public (bool Success, string Message, string Severity, bool LastBackupExists, string OptionalMessage) DryRunBackup() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; var appBasePath = Path.GetDirectoryName(settingsUtils.GetSettingsFilePath()); string settingsBackupAndRestoreDir = GetSettingsBackupAndRestoreDir(); var results = BackupSettings(appBasePath, settingsBackupAndRestoreDir, true); diff --git a/src/settings-ui/Settings.UI.Library/SettingsUtils.cs b/src/settings-ui/Settings.UI.Library/SettingsUtils.cs index 6109df0646..eb4169f422 100644 --- a/src/settings-ui/Settings.UI.Library/SettingsUtils.cs +++ b/src/settings-ui/Settings.UI.Library/SettingsUtils.cs @@ -22,7 +22,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library private readonly ISettingsPath _settingsPath; private readonly JsonSerializerOptions _serializerOptions; - public SettingsUtils() + /// + /// Gets the default instance of the class for general use. + /// Same as instantiating a new instance with the constructor with a new object as the first argument and null as the second argument. + /// + /// For using in tests, you should use one of the public constructors. + public static SettingsUtils Default { get; } = new SettingsUtils(); + + private SettingsUtils() : this(new FileSystem()) { } @@ -234,7 +241,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library public static (bool Success, string Message, string Severity, bool LastBackupExists, string OptionalMessage) BackupSettings() { var settingsBackupAndRestoreUtilsX = SettingsBackupAndRestoreUtils.Instance; - var settingsUtils = new SettingsUtils(); + var settingsUtils = Default; var appBasePath = Path.GetDirectoryName(settingsUtils._settingsPath.GetSettingsPath(string.Empty, string.Empty)); string settingsBackupAndRestoreDir = settingsBackupAndRestoreUtilsX.GetSettingsBackupAndRestoreDir(); @@ -247,7 +254,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library public static (bool Success, string Message, string Severity) RestoreSettings() { var settingsBackupAndRestoreUtilsX = SettingsBackupAndRestoreUtils.Instance; - var settingsUtils = new SettingsUtils(); + var settingsUtils = Default; var appBasePath = Path.GetDirectoryName(settingsUtils._settingsPath.GetSettingsPath(string.Empty, string.Empty)); string settingsBackupAndRestoreDir = settingsBackupAndRestoreUtilsX.GetSettingsBackupAndRestoreDir(); return settingsBackupAndRestoreUtilsX.RestoreSettings(appBasePath, settingsBackupAndRestoreDir); diff --git a/src/settings-ui/Settings.UI.Library/Utilities/GetSettingCommandLineCommand.cs b/src/settings-ui/Settings.UI.Library/Utilities/GetSettingCommandLineCommand.cs index a4ad1d1862..5780b95392 100644 --- a/src/settings-ui/Settings.UI.Library/Utilities/GetSettingCommandLineCommand.cs +++ b/src/settings-ui/Settings.UI.Library/Utilities/GetSettingCommandLineCommand.cs @@ -48,7 +48,7 @@ public sealed class GetSettingCommandLineCommand var modulesSettings = new Dictionary>(); var settingsAssembly = CommandLineUtils.GetSettingsAssembly(); - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; var enabledModules = SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Enabled; diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs index baf46827c4..19e2ca3b67 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs @@ -67,7 +67,7 @@ namespace ViewModelTests using (var viewModel = new ColorPickerViewModel( ISettingsUtilsMocks.GetStubSettingsUtils().Object, SettingsRepository.GetInstance(ISettingsUtilsMocks.GetStubSettingsUtils().Object), - SettingsRepository.GetInstance(new SettingsUtils()), + SettingsRepository.GetInstance(SettingsUtils.Default), ColorPickerIsEnabledByDefaultIPC)) { Assert.IsTrue(viewModel.IsEnabled); diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/FancyZones.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/FancyZones.cs index de6421785b..bf8f9396c4 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/FancyZones.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/FancyZones.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.IO.Abstractions; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Library; @@ -120,7 +121,7 @@ namespace ViewModelTests [TestMethod] public void IsEnabledShouldDisableModuleWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); Func sendMockIPCConfigMSG = msg => { @@ -140,7 +141,7 @@ namespace ViewModelTests [TestMethod] public void ShiftDragShouldSetValue2FalseWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -158,7 +159,7 @@ namespace ViewModelTests [TestMethod] public void OverrideSnapHotkeysShouldSetValue2TrueWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -176,7 +177,7 @@ namespace ViewModelTests [TestMethod] public void MoveWindowsAcrossMonitorsShouldSetValue2TrueWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -194,7 +195,7 @@ namespace ViewModelTests [TestMethod] public void MoveWindowsBasedOnPositionShouldSetValue2TrueWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -217,7 +218,7 @@ namespace ViewModelTests [TestMethod] public void QuickLayoutSwitchShouldSetValue2FalseWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -235,7 +236,7 @@ namespace ViewModelTests [TestMethod] public void FlashZonesOnQuickSwitchShouldSetValue2FalseWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -253,7 +254,7 @@ namespace ViewModelTests [TestMethod] public void MakeDraggedWindowsTransparentShouldSetValue2TrueWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -271,7 +272,7 @@ namespace ViewModelTests [TestMethod] public void MouseSwitchShouldSetValue2TrueWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -289,7 +290,7 @@ namespace ViewModelTests [TestMethod] public void DisplayOrWorkAreaChangeMoveWindowsShouldSetValue2FalseWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -307,7 +308,7 @@ namespace ViewModelTests [TestMethod] public void ZoneSetChangeMoveWindowsShouldSetValue2TrueWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -325,7 +326,7 @@ namespace ViewModelTests [TestMethod] public void AppLastZoneMoveWindowsShouldSetValue2TrueWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -343,7 +344,7 @@ namespace ViewModelTests [TestMethod] public void OpenWindowOnActiveMonitorShouldSetValue2TrueWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -361,7 +362,7 @@ namespace ViewModelTests [TestMethod] public void RestoreSizeShouldSetValue2TrueWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -379,7 +380,7 @@ namespace ViewModelTests [TestMethod] public void UseCursorPosEditorStartupScreenShouldSetValue2FalseWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -397,7 +398,7 @@ namespace ViewModelTests [TestMethod] public void ShowOnAllMonitorsShouldSetValue2TrueWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -415,7 +416,7 @@ namespace ViewModelTests [TestMethod] public void SpanZonesAcrossMonitorsShouldSetValue2TrueWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -433,7 +434,7 @@ namespace ViewModelTests [TestMethod] public void OverlappingZonesAlgorithmIndexShouldSetValue2AnotherWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -451,7 +452,7 @@ namespace ViewModelTests [TestMethod] public void AllowChildWindowsToSnapShouldSetValue2TrueWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -469,7 +470,7 @@ namespace ViewModelTests [TestMethod] public void DisableRoundCornersOnSnapShouldSetValue2TrueWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -487,7 +488,7 @@ namespace ViewModelTests [TestMethod] public void ZoneHighlightColorShouldSetColorValue2WhiteWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -505,7 +506,7 @@ namespace ViewModelTests [TestMethod] public void ZoneBorderColorShouldSetColorValue2WhiteWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -523,7 +524,7 @@ namespace ViewModelTests [TestMethod] public void ZoneInActiveColorShouldSetColorValue2WhiteWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -541,7 +542,7 @@ namespace ViewModelTests [TestMethod] public void ExcludedAppsShouldSetColorValue2WhiteWhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); @@ -559,7 +560,7 @@ namespace ViewModelTests [TestMethod] public void HighlightOpacityShouldSetOpacityValueTo60WhenSuccessful() { - Mock mockSettingsUtils = new Mock(); + Mock mockSettingsUtils = new Mock(new FileSystem(), null); // arrange FancyZonesViewModel viewModel = new FancyZonesViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), sendMockIPCConfigMSG, FancyZonesTestFolderName); diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs index 3613d0cfa3..897fc2bec6 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.IO.Abstractions; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Library; @@ -69,7 +70,7 @@ namespace ViewModelTests [TestMethod] public void IsEnabledShouldEnableModuleWhenSuccessful() { - var settingsUtilsMock = new Mock(); + var settingsUtilsMock = new Mock(new FileSystem(), null); // Assert // Initialize mock function of sending IPC message. diff --git a/src/settings-ui/Settings.UI/Helpers/HotkeyConflictIgnoreHelper.cs b/src/settings-ui/Settings.UI/Helpers/HotkeyConflictIgnoreHelper.cs index d2e737180a..1397fca0b7 100644 --- a/src/settings-ui/Settings.UI/Helpers/HotkeyConflictIgnoreHelper.cs +++ b/src/settings-ui/Settings.UI/Helpers/HotkeyConflictIgnoreHelper.cs @@ -22,7 +22,7 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers static HotkeyConflictIgnoreHelper() { - _settingsUtils = new SettingsUtils(); + _settingsUtils = SettingsUtils.Default; _generalSettingsRepository = SettingsRepository.GetInstance(_settingsUtils); } diff --git a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs index 19cd75b022..3189f15b31 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs @@ -133,7 +133,7 @@ namespace Microsoft.PowerToys.Settings.UI var settingValue = cmdArgs[3]; try { - SetSettingCommandLineCommand.Execute(settingName, settingValue, new SettingsUtils()); + SetSettingCommandLineCommand.Execute(settingName, settingValue, SettingsUtils.Default); } catch (Exception ex) { @@ -151,7 +151,7 @@ namespace Microsoft.PowerToys.Settings.UI { using (var settings = JsonDocument.Parse(File.ReadAllText(ipcFileName))) { - SetAdditionalSettingsCommandLineCommand.Execute(moduleName, settings, new SettingsUtils()); + SetAdditionalSettingsCommandLineCommand.Execute(moduleName, settings, SettingsUtils.Default); } } catch (Exception ex) @@ -357,7 +357,7 @@ namespace Microsoft.PowerToys.Settings.UI return 0; } - private static ISettingsUtils settingsUtils = new SettingsUtils(); + private static ISettingsUtils settingsUtils = SettingsUtils.Default; private static ThemeService themeService = new ThemeService(SettingsRepository.GetInstance(settingsUtils)); public static ThemeService ThemeService => themeService; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/ShortcutConflictWindow.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/ShortcutConflictWindow.xaml.cs index b9bee4ff08..19a6cb06ad 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/ShortcutConflictWindow.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/ShortcutConflictWindow.xaml.cs @@ -26,7 +26,7 @@ namespace Microsoft.PowerToys.Settings.UI.SettingsXAML.Controls.Dashboard public ShortcutConflictWindow() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new ShortcutConflictViewModel( settingsUtils, SettingsRepository.GetInstance(settingsUtils), diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Flyout/AppsListPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Flyout/AppsListPage.xaml.cs index b58636f41b..fd034239e1 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Flyout/AppsListPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Flyout/AppsListPage.xaml.cs @@ -24,7 +24,7 @@ namespace Microsoft.PowerToys.Settings.UI.Flyout { this.InitializeComponent(); - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new AllAppsViewModel(SettingsRepository.GetInstance(settingsUtils), Views.ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs index 51219309e0..f82da1466c 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs @@ -27,7 +27,7 @@ namespace Microsoft.PowerToys.Settings.UI.Flyout public LaunchPage() { this.InitializeComponent(); - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new LauncherViewModel(SettingsRepository.GetInstance(settingsUtils), Views.ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } @@ -51,7 +51,7 @@ namespace Microsoft.PowerToys.Settings.UI.Flyout break; case ModuleType.EnvironmentVariables: // Launch Environment Variables { - bool launchAdmin = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.LaunchAdministrator; + bool launchAdmin = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.LaunchAdministrator; string eventName = !App.IsElevated && launchAdmin ? Constants.ShowEnvironmentVariablesAdminSharedEvent() : Constants.ShowEnvironmentVariablesSharedEvent(); @@ -74,7 +74,7 @@ namespace Microsoft.PowerToys.Settings.UI.Flyout case ModuleType.Hosts: // Launch Hosts { - bool launchAdmin = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.LaunchAdministrator; + bool launchAdmin = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.LaunchAdministrator; string eventName = !App.IsElevated && launchAdmin ? Constants.ShowHostsAdminSharedEvent() : Constants.ShowHostsSharedEvent(); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs index 90b2577268..27d5456418 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs @@ -81,7 +81,7 @@ namespace Microsoft.PowerToys.Settings.UI // open main window ShellPage.SetUpdatingGeneralSettingsCallback((ModuleType moduleType, bool isEnabled) => { - SettingsRepository repository = SettingsRepository.GetInstance(new SettingsUtils()); + SettingsRepository repository = SettingsRepository.GetInstance(SettingsUtils.Default); GeneralSettings generalSettingsConfig = repository.SettingsConfig; bool needToUpdate = ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, moduleType) != isEnabled; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeAdvancedPaste.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeAdvancedPaste.xaml.cs index 52cbc6cef2..73e25ed856 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeAdvancedPaste.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeAdvancedPaste.xaml.cs @@ -35,9 +35,9 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views protected override void OnNavigatedTo(NavigationEventArgs e) { ViewModel.LogOpeningModuleEvent(); - AdvancedPasteUIHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.AdvancedPasteUIShortcut.GetKeysList(); - PasteAsPlainTextHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.PasteAsPlainTextShortcut.GetKeysList(); - PasteAsMarkdownHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.PasteAsMarkdownShortcut.GetKeysList(); + AdvancedPasteUIHotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.AdvancedPasteUIShortcut.GetKeysList(); + PasteAsPlainTextHotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.PasteAsPlainTextShortcut.GetKeysList(); + PasteAsMarkdownHotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.PasteAsMarkdownShortcut.GetKeysList(); // TODO(stefan): Check how to remove additional space if item is set to Collapsed. if (PasteAsMarkdownHotkeyControl.Keys.Count > 0) @@ -45,7 +45,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views PasteAsMarkdownHotkeyControl.Visibility = Microsoft.UI.Xaml.Visibility.Visible; } - PasteAsJsonHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.PasteAsJsonShortcut.GetKeysList(); + PasteAsJsonHotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.PasteAsJsonShortcut.GetKeysList(); if (PasteAsJsonHotkeyControl.Keys.Count > 0) { PasteAsJsonHotkeyControl.Visibility = Microsoft.UI.Xaml.Visibility.Visible; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeAlwaysOnTop.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeAlwaysOnTop.xaml.cs index d22392e717..08fb0f8a80 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeAlwaysOnTop.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeAlwaysOnTop.xaml.cs @@ -35,7 +35,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views protected override void OnNavigatedTo(NavigationEventArgs e) { ViewModel.LogOpeningModuleEvent(); - HotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.Hotkey.Value.GetKeysList(); + HotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.Hotkey.Value.GetKeysList(); } protected override void OnNavigatedFrom(NavigationEventArgs e) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeColorPicker.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeColorPicker.xaml.cs index 444b2d7295..8fd2bfb28d 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeColorPicker.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeColorPicker.xaml.cs @@ -50,7 +50,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views protected override void OnNavigatedTo(NavigationEventArgs e) { ViewModel.LogOpeningModuleEvent(); - ColorPickerSettings settings = new SettingsUtils().GetSettingsOrDefault(ColorPickerSettings.ModuleName, settingsUpgrader: ColorPickerSettings.UpgradeSettings); + ColorPickerSettings settings = SettingsUtils.Default.GetSettingsOrDefault(ColorPickerSettings.ModuleName, settingsUpgrader: ColorPickerSettings.UpgradeSettings); HotkeyControl.Keys = settings.Properties.ActivationShortcut.GetKeysList(); } diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeCropAndLock.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeCropAndLock.xaml.cs index be05925ec3..914dd647f6 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeCropAndLock.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeCropAndLock.xaml.cs @@ -35,8 +35,8 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views protected override void OnNavigatedTo(NavigationEventArgs e) { ViewModel.LogOpeningModuleEvent(); - ReparentHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.ReparentHotkey.Value.GetKeysList(); - ThumbnailHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.ThumbnailHotkey.Value.GetKeysList(); + ReparentHotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ReparentHotkey.Value.GetKeysList(); + ThumbnailHotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ThumbnailHotkey.Value.GetKeysList(); } protected override void OnNavigatedFrom(NavigationEventArgs e) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeEnvironmentVariables.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeEnvironmentVariables.xaml.cs index af20978b42..3e2b66b9d6 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeEnvironmentVariables.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeEnvironmentVariables.xaml.cs @@ -37,7 +37,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views private void Launch_EnvironmentVariables_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) { - bool launchAdmin = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.LaunchAdministrator; + bool launchAdmin = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.LaunchAdministrator; string eventName = !App.IsElevated && launchAdmin ? Constants.ShowEnvironmentVariablesAdminSharedEvent() : Constants.ShowEnvironmentVariablesSharedEvent(); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeFancyZones.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeFancyZones.xaml.cs index ccbc5c8cc7..5308336e16 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeFancyZones.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeFancyZones.xaml.cs @@ -35,7 +35,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views protected override void OnNavigatedTo(NavigationEventArgs e) { ViewModel.LogOpeningModuleEvent(); - HotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.FancyzonesEditorHotkey.Value.GetKeysList(); + HotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.FancyzonesEditorHotkey.Value.GetKeysList(); } protected override void OnNavigatedFrom(NavigationEventArgs e) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeHosts.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeHosts.xaml.cs index d47af96c7f..0a98df472e 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeHosts.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeHosts.xaml.cs @@ -37,7 +37,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views private void Launch_Hosts_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) { - bool launchAdmin = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.LaunchAdministrator; + bool launchAdmin = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.LaunchAdministrator; string eventName = !App.IsElevated && launchAdmin ? Constants.ShowHostsAdminSharedEvent() : Constants.ShowHostsSharedEvent(); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeMeasureTool.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeMeasureTool.xaml.cs index 7ca7739883..e8a24b9fc5 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeMeasureTool.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeMeasureTool.xaml.cs @@ -38,7 +38,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views protected override void OnNavigatedTo(NavigationEventArgs e) { ViewModel.LogOpeningModuleEvent(); - HotkeyActivation.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.ActivationShortcut.GetKeysList(); + HotkeyActivation.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ActivationShortcut.GetKeysList(); } protected override void OnNavigatedFrom(NavigationEventArgs e) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverviewAlternate.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverviewAlternate.xaml.cs index d0ae488347..cbd05fc7ed 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverviewAlternate.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverviewAlternate.xaml.cs @@ -21,10 +21,10 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Overview]); DataContext = ViewModel; - FancyZonesHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.FancyzonesEditorHotkey.Value.GetKeysList(); - RunHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.OpenPowerLauncher.GetKeysList(); - ColorPickerHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.ActivationShortcut.GetKeysList(); - AlwaysOnTopHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.Hotkey.Value.GetKeysList(); + FancyZonesHotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.FancyzonesEditorHotkey.Value.GetKeysList(); + RunHotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.OpenPowerLauncher.GetKeysList(); + ColorPickerHotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ActivationShortcut.GetKeysList(); + AlwaysOnTopHotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.Hotkey.Value.GetKeysList(); } private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobePeek.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobePeek.xaml.cs index 7817b13ca4..6782f4adc7 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobePeek.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobePeek.xaml.cs @@ -38,7 +38,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views protected override void OnNavigatedTo(NavigationEventArgs e) { ViewModel.LogOpeningModuleEvent(); - HotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.ActivationShortcut.GetKeysList(); + HotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ActivationShortcut.GetKeysList(); } protected override void OnNavigatedFrom(NavigationEventArgs e) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobePowerOCR.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobePowerOCR.xaml.cs index 7fef84d2b9..cf607d370e 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobePowerOCR.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobePowerOCR.xaml.cs @@ -35,7 +35,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views protected override void OnNavigatedTo(NavigationEventArgs e) { ViewModel.LogOpeningModuleEvent(); - HotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.ActivationShortcut.GetKeysList(); + HotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ActivationShortcut.GetKeysList(); } protected override void OnNavigatedFrom(NavigationEventArgs e) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeRun.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeRun.xaml.cs index 3cb8593922..796e05bdea 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeRun.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeRun.xaml.cs @@ -54,7 +54,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views { ViewModel.LogOpeningModuleEvent(); - HotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.OpenPowerLauncher.GetKeysList(); + HotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.OpenPowerLauncher.GetKeysList(); } protected override void OnNavigatedFrom(NavigationEventArgs e) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShellPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShellPage.xaml.cs index 8b01ab0bd0..7f8531286c 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShellPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShellPage.xaml.cs @@ -51,7 +51,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views public ObservableCollection Modules { get; } - private static ISettingsUtils settingsUtils = new SettingsUtils(); + private static ISettingsUtils settingsUtils = SettingsUtils.Default; /* NOTE: Experimentation for OOBE is currently turned off on server side. Keeping this code in a comment to allow future experiments. private bool ExperimentationToggleSwitchEnabled { get; set; } = true; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShortcutGuide.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShortcutGuide.xaml.cs index 5702ddcb9f..e721deefd6 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShortcutGuide.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShortcutGuide.xaml.cs @@ -56,7 +56,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views protected override void OnNavigatedTo(NavigationEventArgs e) { ViewModel.LogOpeningModuleEvent(); - var settingsProperties = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties; + var settingsProperties = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties; if ((bool)settingsProperties.UseLegacyPressWinKeyBehavior.Value) { diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeWorkspaces.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeWorkspaces.xaml.cs index 772807f735..98a8bccb84 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeWorkspaces.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeWorkspaces.xaml.cs @@ -38,7 +38,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views protected override void OnNavigatedTo(NavigationEventArgs e) { ViewModel.LogOpeningModuleEvent(); - HotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.Hotkey.Value.GetKeysList(); + HotkeyControl.Keys = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.Hotkey.Value.GetKeysList(); } protected override void OnNavigatedFrom(NavigationEventArgs e) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs index 7b267107ee..faf31b0d4d 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs @@ -44,7 +44,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public AdvancedPastePage() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new AdvancedPasteViewModel( settingsUtils, SettingsRepository.GetInstance(settingsUtils), diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/AlwaysOnTopPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/AlwaysOnTopPage.xaml.cs index c5d99e8e98..95ba8b595f 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/AlwaysOnTopPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/AlwaysOnTopPage.xaml.cs @@ -15,7 +15,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public AlwaysOnTopPage() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new AlwaysOnTopViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; InitializeComponent(); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/AwakePage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/AwakePage.xaml.cs index 91b2b0b781..f52e96fb8f 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/AwakePage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/AwakePage.xaml.cs @@ -36,7 +36,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views { _dispatcherQueue = DispatcherQueue.GetForCurrentThread(); _fileSystem = new FileSystem(); - _settingsUtils = new SettingsUtils(); + _settingsUtils = SettingsUtils.Default; _sendConfigMsg = ShellPage.SendDefaultIPCMessage; ViewModel = new AwakeViewModel(); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/CmdPalPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/CmdPalPage.xaml.cs index ea318100f0..90dec43398 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/CmdPalPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/CmdPalPage.xaml.cs @@ -19,7 +19,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public CmdPalPage() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new CmdPalViewModel( settingsUtils, SettingsRepository.GetInstance(settingsUtils), diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml.cs index 9e0ba6be07..652db49f4b 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml.cs @@ -27,7 +27,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public ColorPickerPage() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new ColorPickerViewModel( settingsUtils, SettingsRepository.GetInstance(settingsUtils), diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/CropAndLockPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/CropAndLockPage.xaml.cs index 67ef238645..5c174b1f98 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/CropAndLockPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/CropAndLockPage.xaml.cs @@ -15,7 +15,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public CropAndLockPage() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new CropAndLockViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; InitializeComponent(); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs index 0d7273f924..522f8fbaf4 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs @@ -34,7 +34,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public DashboardPage() { InitializeComponent(); - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new DashboardViewModel( SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/EnvironmentVariablesPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/EnvironmentVariablesPage.xaml.cs index 0772869249..a3e17ac491 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/EnvironmentVariablesPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/EnvironmentVariablesPage.xaml.cs @@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public EnvironmentVariablesPage() { InitializeComponent(); - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new EnvironmentVariablesViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, App.IsElevated); } diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml.cs index e1d7f7ea4b..ddf7c1e8ee 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml.cs @@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public FancyZonesPage() { InitializeComponent(); - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new FancyZonesViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; Loaded += (s, e) => ViewModel.OnPageLoaded(); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/FileLocksmithPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/FileLocksmithPage.xaml.cs index 6020917b69..8f78ce2c8c 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/FileLocksmithPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/FileLocksmithPage.xaml.cs @@ -15,7 +15,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public FileLocksmithPage() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new FileLocksmithViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; InitializeComponent(); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml.cs index 56eb73c6ef..5e84711b86 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml.cs @@ -38,7 +38,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views // Load string resources var loader = Helpers.ResourceLoaderInstance.ResourceLoader; - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; Action stateUpdatingAction = () => { diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/HostsPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/HostsPage.xaml.cs index 19375d90f7..b65f32d520 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/HostsPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/HostsPage.xaml.cs @@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public HostsPage() { InitializeComponent(); - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new HostsViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, App.IsElevated); BackupsCountInputSettingsCard.Header = ResourceLoaderInstance.ResourceLoader.GetString("Hosts_Backup_CountInput_Header"); BackupsCountInputSettingsCard.Description = ResourceLoaderInstance.ResourceLoader.GetString("Hosts_Backup_CountInput_Description"); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml.cs index 18e1aacc15..6a2068d5a8 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml.cs @@ -21,7 +21,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public ImageResizerPage() { InitializeComponent(); - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; var resourceLoader = ResourceLoaderInstance.ResourceLoader; Func loader = resourceLoader.GetString; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/KeyboardManagerPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/KeyboardManagerPage.xaml.cs index e32f5aa9cb..fce4dfc718 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/KeyboardManagerPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/KeyboardManagerPage.xaml.cs @@ -28,7 +28,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public KeyboardManagerPage() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new KeyboardManagerViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, FilterRemapKeysList); watcher = Helper.GetFileWatcher( diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml.cs index 974447a20e..dcd40fdbc7 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml.cs @@ -40,7 +40,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public LightSwitchPage() { - this.settingsUtils = new SettingsUtils(); + this.settingsUtils = SettingsUtils.Default; this.sendConfigMsg = ShellPage.SendDefaultIPCMessage; this.generalSettingsRepository = SettingsRepository.GetInstance(this.settingsUtils); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/MeasureToolPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/MeasureToolPage.xaml.cs index bf516b557b..8f80f1c13b 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/MeasureToolPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/MeasureToolPage.xaml.cs @@ -17,7 +17,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public MeasureToolPage() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new MeasureToolViewModel( settingsUtils, SettingsRepository.GetInstance(settingsUtils), diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml.cs index ac4c7cc71d..fa34ca5293 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml.cs @@ -22,7 +22,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views { // By mistake, the first release of Find My Mouse was saving settings in two places at the same time. // Delete the wrong path for Find My Mouse settings. - var tempSettingsUtils = new SettingsUtils(); + var tempSettingsUtils = SettingsUtils.Default; if (tempSettingsUtils.SettingsExists("Find My Mouse")) { var settingsFilePath = tempSettingsUtils.GetSettingsFilePath("Find My Mouse"); @@ -34,7 +34,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views { } - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new MouseUtilsViewModel( settingsUtils, SettingsRepository.GetInstance(settingsUtils), diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseWithoutBordersPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseWithoutBordersPage.xaml.cs index 296a9a3deb..2db0d7a5d6 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseWithoutBordersPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseWithoutBordersPage.xaml.cs @@ -33,7 +33,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public MouseWithoutBordersPage() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new MouseWithoutBordersViewModel( settingsUtils, SettingsRepository.GetInstance(settingsUtils), diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml.cs index daa4dca2b5..997f6c7771 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml.cs @@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public NewPlusPage() { InitializeComponent(); - var settings_utils = new SettingsUtils(); + var settings_utils = SettingsUtils.Default; ViewModel = new NewPlusViewModel(settings_utils, SettingsRepository.GetInstance(settings_utils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/PeekPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/PeekPage.xaml.cs index 1a845e25ec..b848ae86dd 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/PeekPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/PeekPage.xaml.cs @@ -15,7 +15,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public PeekPage() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new PeekViewModel( settingsUtils, SettingsRepository.GetInstance(settingsUtils), diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml.cs index fa666dd242..0f60a8dcd3 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml.cs @@ -17,7 +17,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public PowerAccentPage() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new PowerAccentViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; this.InitializeComponent(); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml.cs index c734535d49..19973a69bc 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml.cs @@ -34,7 +34,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public PowerLauncherPage() { InitializeComponent(); - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; _lastIPCMessageSentTick = Environment.TickCount; PowerLauncherSettings settings = SettingsRepository.GetInstance(settingsUtils)?.SettingsConfig; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerOcrPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerOcrPage.xaml.cs index 27769b92ce..9198d338ef 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerOcrPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerOcrPage.xaml.cs @@ -15,7 +15,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public PowerOcrPage() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new PowerOcrViewModel( settingsUtils, SettingsRepository.GetInstance(settingsUtils), diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerPreviewPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerPreviewPage.xaml.cs index 9d56c686e3..ecc960651b 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerPreviewPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerPreviewPage.xaml.cs @@ -19,7 +19,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public PowerPreviewPage() { InitializeComponent(); - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new PowerPreviewViewModel(SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerRenamePage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerRenamePage.xaml.cs index d97b83c499..10013b84a3 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerRenamePage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerRenamePage.xaml.cs @@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public PowerRenamePage() { InitializeComponent(); - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new PowerRenameViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/RegistryPreviewPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/RegistryPreviewPage.xaml.cs index 8c1ed319a3..41d814e48b 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/RegistryPreviewPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/RegistryPreviewPage.xaml.cs @@ -14,7 +14,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public RegistryPreviewPage() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new RegistryPreviewViewModel( SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/ShellPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/ShellPage.xaml.cs index 06f7e222ce..853b66f164 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/ShellPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/ShellPage.xaml.cs @@ -151,7 +151,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views { InitializeComponent(); SetWindowTitle(); - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new ShellViewModel(SettingsRepository.GetInstance(settingsUtils)); DataContext = ViewModel; ShellHandler = this; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/ShortcutGuidePage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/ShortcutGuidePage.xaml.cs index ffca0184c7..feb0e4837d 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/ShortcutGuidePage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/ShortcutGuidePage.xaml.cs @@ -17,7 +17,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views { InitializeComponent(); - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new ShortcutGuideViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/WorkspacesPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/WorkspacesPage.xaml.cs index 0b0ca571c4..3028b46ea1 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/WorkspacesPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/WorkspacesPage.xaml.cs @@ -15,7 +15,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public WorkspacesPage() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new WorkspacesViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; InitializeComponent(); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/ZoomItPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/ZoomItPage.xaml.cs index 9018eb64e8..043ca7df8c 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/ZoomItPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/ZoomItPage.xaml.cs @@ -104,7 +104,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public ZoomItPage() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; ViewModel = new ZoomItViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, PickFileDialog, PickFontDialog); DataContext = ViewModel; InitializeComponent(); diff --git a/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs index b0520dd38d..1afe76897e 100644 --- a/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs @@ -251,7 +251,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels if (dashboardListItem.Tag == ModuleType.NewPlus && dashboardListItem.IsEnabled == true) { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; var settings = NewPlusViewModel.LoadSettings(settingsUtils); NewPlusViewModel.CopyTemplateExamples(settings.Properties.TemplateLocation.Value); } @@ -390,7 +390,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsAlwaysOnTop() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); var list = new List { new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("AlwaysOnTop_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.Hotkey.Value.GetKeysList() }, @@ -411,7 +411,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsColorPicker() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); var settings = moduleSettingsRepository.SettingsConfig; var hotkey = settings.Properties.ActivationShortcut; var list = new List @@ -423,7 +423,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsLightSwitch() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); var settings = moduleSettingsRepository.SettingsConfig; var list = new List { @@ -434,7 +434,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsCropAndLock() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); var settings = moduleSettingsRepository.SettingsConfig; var list = new List { @@ -455,7 +455,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsFancyZones() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); var settings = moduleSettingsRepository.SettingsConfig; string activationMode = $"{resourceLoader.GetString(settings.Properties.FancyzonesShiftDrag.Value ? "FancyZones_ActivationShiftDrag" : "FancyZones_ActivationNoShiftDrag")}."; @@ -470,7 +470,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsFindMyMouse() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); string shortDescription = resourceLoader.GetString("FindMyMouse_ShortDescription"); var settings = moduleSettingsRepository.SettingsConfig; var activationMethod = settings.Properties.ActivationMethod.Value; @@ -508,7 +508,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsMouseHighlighter() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); var list = new List { new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("MouseHighlighter_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.ActivationShortcut.GetKeysList() }, @@ -518,7 +518,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsMouseJump() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); var list = new List { new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("MouseJump_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.ActivationShortcut.GetKeysList() }, @@ -528,7 +528,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsMousePointerCrosshairs() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); var list = new List { new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("MouseCrosshairs_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.ActivationShortcut.GetKeysList() }, @@ -538,7 +538,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsAdvancedPaste() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); var list = new List { new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("AdvancedPasteUI_Shortcut/Header"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.AdvancedPasteUIShortcut.GetKeysList() }, @@ -560,7 +560,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsPeek() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); var list = new List { new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("Peek_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.ActivationShortcut.GetKeysList() }, @@ -570,7 +570,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsPowerLauncher() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); var list = new List { new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("Run_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.OpenPowerLauncher.GetKeysList() }, @@ -580,7 +580,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsPowerAccent() { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; PowerAccentSettings moduleSettings = settingsUtils.GetSettingsOrDefault(PowerAccentSettings.ModuleName); var activationMethod = moduleSettings.Properties.ActivationKey; string activation = string.Empty; @@ -600,7 +600,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsWorkspaces() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); var settings = moduleSettingsRepository.SettingsConfig; var list = new List @@ -622,7 +622,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsMeasureTool() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); var list = new List { new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("ScreenRuler_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.ActivationShortcut.GetKeysList() }, @@ -632,7 +632,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsShortcutGuide() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); var shortcut = moduleSettingsRepository.SettingsConfig.Properties.UseLegacyPressWinKeyBehavior.Value ? new List { 92 } // Right Windows key code @@ -647,7 +647,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private ObservableCollection GetModuleItemsPowerOCR() { - ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(new SettingsUtils()); + ISettingsRepository moduleSettingsRepository = SettingsRepository.GetInstance(SettingsUtils.Default); var list = new List { new DashboardModuleShortcutItem() { Label = resourceLoader.GetString("PowerOcr_ShortDescription"), Shortcut = moduleSettingsRepository.SettingsConfig.Properties.ActivationShortcut.GetKeysList() }, @@ -662,14 +662,14 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private void EnvironmentVariablesLaunchClicked(object sender, RoutedEventArgs e) { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; var environmentVariablesViewModel = new EnvironmentVariablesViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, App.IsElevated); environmentVariablesViewModel.Launch(); } private void HostLaunchClicked(object sender, RoutedEventArgs e) { - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; var hostsViewModel = new HostsViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, App.IsElevated); hostsViewModel.Launch(); } diff --git a/src/settings-ui/Settings.UI/ViewModels/Flyout/LauncherViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/Flyout/LauncherViewModel.cs index a1db1e2dd0..7adc4bb933 100644 --- a/src/settings-ui/Settings.UI/ViewModels/Flyout/LauncherViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/Flyout/LauncherViewModel.cs @@ -88,12 +88,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels { return moduleType switch { - ModuleType.ColorPicker => SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.ActivationShortcut.ToString(), - ModuleType.FancyZones => SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.FancyzonesEditorHotkey.Value.ToString(), - ModuleType.PowerLauncher => SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.OpenPowerLauncher.ToString(), - ModuleType.PowerOCR => SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.ActivationShortcut.ToString(), - ModuleType.Workspaces => SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.Hotkey.Value.ToString(), - ModuleType.MeasureTool => SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.ActivationShortcut.ToString(), + ModuleType.ColorPicker => SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ActivationShortcut.ToString(), + ModuleType.FancyZones => SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.FancyzonesEditorHotkey.Value.ToString(), + ModuleType.PowerLauncher => SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.OpenPowerLauncher.ToString(), + ModuleType.PowerOCR => SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ActivationShortcut.ToString(), + ModuleType.Workspaces => SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.Hotkey.Value.ToString(), + ModuleType.MeasureTool => SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.ActivationShortcut.ToString(), ModuleType.ShortcutGuide => GetShortcutGuideToolTip(), _ => string.Empty, }; @@ -111,7 +111,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private string GetShortcutGuideToolTip() { - var shortcutGuideSettings = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig; + var shortcutGuideSettings = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig; return shortcutGuideSettings.Properties.UseLegacyPressWinKeyBehavior.Value ? "Win" : shortcutGuideSettings.Properties.OpenShortcutGuide.ToString(); diff --git a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs index 7a6a7fc284..25ab4db13a 100644 --- a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs @@ -1145,7 +1145,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels _settingsBackupMessage = GetResourceString(results.Message) + results.OptionalMessage; // now we do a dry run to get the results for "setting match" - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; var appBasePath = Path.GetDirectoryName(settingsUtils.GetSettingsFilePath()); settingsBackupAndRestoreUtils.BackupSettings(appBasePath, settingsBackupAndRestoreDir, true); diff --git a/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs index 3aa90cbc24..a48742672c 100644 --- a/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs @@ -274,7 +274,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels try { // Check if the experimentation toggle is enabled in the settings - var settingsUtils = new SettingsUtils(); + var settingsUtils = SettingsUtils.Default; bool isExperimentationEnabled = SettingsRepository.GetInstance(settingsUtils).SettingsConfig.EnableExperimentation; // Only read the registry value if the experimentation toggle is enabled