From b266e336b5f4bbb07738ac1bcfdc8bc9e379bc74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Sto=C5=A1i=C4=87?= Date: Mon, 21 Sep 2020 12:44:16 +0200 Subject: [PATCH 01/22] [Launcher] Use a keyboard hook in the runner to invoke the Launcher (#6660) * Added a keyboard hook to the runner * Update RootKeyboardHook * Enable reading the whole JsonObject property * Renamed RootKeyboardHook to CentralizedKeyboardHook * Fixed build break, changed callback return type to bool * Added Hotkey struct which somehow went missing + Cherry-pick fixes * Reorganized the kb hook * Basic version works * Various fixes * Finishing touches * Fix potential threading issue * int -> size_t * Add default initializers to the Hotkey struct * Added a suggested comment * Unified a constant * Use C# classes instead of native calls for sync * Added a claryfing comment * Use std::move * Renamed a method * Possible fix for compilation errors * Fix a regression * Show a message on failure * Added DISABLE_LOWLEVEL_HOOK support * Allow running Launcher as standalone * Rename string constants --- src/action_runner/action_runner.cpp | 3 +- src/common/interop/interop.h | 5 + src/common/settings_objects.cpp | 5 + src/common/settings_objects.h | 1 + src/common/shared_constants.h | 5 +- .../interface/powertoy_module_interface.h | 31 +++++ .../launcher/Microsoft.Launcher/dllmain.cpp | 87 +++++++++++- .../launcher/PowerLauncher/App.xaml.cs | 38 +++-- .../PowerLauncher/Helper/NativeEventWaiter.cs | 28 ++++ .../PowerLauncher/ViewModel/MainViewModel.cs | 40 +++--- .../UserSettings/Settings.cs | 2 + src/runner/centralized_kb_hook.cpp | 131 ++++++++++++++++++ src/runner/centralized_kb_hook.h | 13 ++ src/runner/main.cpp | 2 + src/runner/pch.h | 1 + src/runner/powertoy_module.cpp | 21 +++ src/runner/powertoy_module.h | 2 + src/runner/runner.vcxproj | 2 + src/runner/runner.vcxproj.filters | 8 +- src/runner/settings_window.cpp | 7 +- 20 files changed, 392 insertions(+), 40 deletions(-) create mode 100644 src/modules/launcher/PowerLauncher/Helper/NativeEventWaiter.cs create mode 100644 src/runner/centralized_kb_hook.cpp create mode 100644 src/runner/centralized_kb_hook.h diff --git a/src/action_runner/action_runner.cpp b/src/action_runner/action_runner.cpp index 36ee2add59..acabf8d767 100644 --- a/src/action_runner/action_runner.cpp +++ b/src/action_runner/action_runner.cpp @@ -190,7 +190,8 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) } else { - params = args[nextArg]; + params += args[nextArg]; + params += L' '; nextArg++; } } diff --git a/src/common/interop/interop.h b/src/common/interop/interop.h index 1de9837b74..45f71e44a6 100644 --- a/src/common/interop/interop.h +++ b/src/common/interop/interop.h @@ -127,5 +127,10 @@ public { public: literal int VK_WIN_BOTH = CommonSharedConstants::VK_WIN_BOTH; + + static String^ PowerLauncherSharedEvent() + { + return gcnew String(CommonSharedConstants::POWER_LAUNCHER_SHARED_EVENT); + } }; } diff --git a/src/common/settings_objects.cpp b/src/common/settings_objects.cpp index a6bdcd58cd..2bd2e8ca68 100644 --- a/src/common/settings_objects.cpp +++ b/src/common/settings_objects.cpp @@ -343,6 +343,11 @@ namespace PowerToysSettings return m_json.GetNamedObject(L"properties").GetNamedObject(property_name).GetNamedObject(L"value"); } + json::JsonObject PowerToyValues::get_raw_json() + { + return m_json; + } + std::wstring PowerToyValues::serialize() { set_version(); diff --git a/src/common/settings_objects.h b/src/common/settings_objects.h index 52f654b962..3f2a212eb6 100644 --- a/src/common/settings_objects.h +++ b/src/common/settings_objects.h @@ -83,6 +83,7 @@ namespace PowerToysSettings std::optional get_int_value(std::wstring_view property_name); std::optional get_string_value(std::wstring_view property_name); std::optional get_json(std::wstring_view property_name); + json::JsonObject get_raw_json(); std::wstring serialize(); void save_to_settings_file(); diff --git a/src/common/shared_constants.h b/src/common/shared_constants.h index f5a5b0b5de..69f5499454 100644 --- a/src/common/shared_constants.h +++ b/src/common/shared_constants.h @@ -8,4 +8,7 @@ namespace CommonSharedConstants // Fake key code to represent VK_WIN. inline const DWORD VK_WIN_BOTH = 0x104; -} \ No newline at end of file + + // Path to the event used by PowerLauncher + const wchar_t POWER_LAUNCHER_SHARED_EVENT[] = L"Local\\PowerToysRunInvokeEvent-30f26ad7-d36d-4c0e-ab02-68bb5ff3c4ab"; +} diff --git a/src/modules/interface/powertoy_module_interface.h b/src/modules/interface/powertoy_module_interface.h index 5732a9b254..3a7e45709e 100644 --- a/src/modules/interface/powertoy_module_interface.h +++ b/src/modules/interface/powertoy_module_interface.h @@ -1,5 +1,7 @@ #pragma once +#include + /* DLL Interface for PowerToys. The powertoy_create() (see below) must return an object that implements this interface. @@ -13,6 +15,7 @@ On the received object, the runner will call: - get_name() to get the name of the PowerToy, - enable() to initialize the PowerToy. + - get_hotkeys() to register the hotkeys the PowerToy uses. While running, the runner might call the following methods between create_powertoy() and destroy(): @@ -20,15 +23,31 @@ - get_config() to get the available configuration settings, - set_config() to set various settings, - call_custom_action() when the user selects clicks a custom action in settings, + - get_hotkeys() when the settings change, to make sure the hotkey(s) are up to date. + - on_hotkey() when the corresponding hotkey is pressed. When terminating, the runner will: - call destroy() which should free all the memory and delete the PowerToy object, - unload the DLL. + + The runner will call on_hotkey() even if the module is disabled. */ class PowertoyModuleIface { public: + /* Describes a hotkey which can trigger an action in the PowerToy */ + struct Hotkey + { + bool win = false; + bool ctrl = false; + bool shift = false; + bool alt = false; + unsigned char key = 0; + + std::strong_ordering operator<=>(const Hotkey&) const = default; + }; + /* Returns the name of the PowerToy, this will be cached by the runner. */ virtual const wchar_t* get_name() = 0; /* Fills a buffer with the available configuration settings. @@ -49,6 +68,18 @@ public: virtual bool is_enabled() = 0; /* Destroy the PowerToy and free all memory. */ virtual void destroy() = 0; + + /* Get the list of hotkeys. Should return the number of available hotkeys and + * fill up the buffer to the minimum of the number of hotkeys and its size. + * Modules do not need to override this method, it will return zero by default. + * This method is called even when the module is disabled. + */ + virtual size_t get_hotkeys(Hotkey* buffer, size_t buffer_size) { return 0; } + + /* Called when one of the registered hotkeys is pressed. Should return true + * if the key press is to be swallowed. + */ + virtual bool on_hotkey(size_t hotkeyId) { return false; } }; /* diff --git a/src/modules/launcher/Microsoft.Launcher/dllmain.cpp b/src/modules/launcher/Microsoft.Launcher/dllmain.cpp index b61bfcd8f3..45f46b41a0 100644 --- a/src/modules/launcher/Microsoft.Launcher/dllmain.cpp +++ b/src/modules/launcher/Microsoft.Launcher/dllmain.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "trace.h" #include "Generated Files/resource.h" #include @@ -10,7 +11,14 @@ extern "C" IMAGE_DOS_HEADER __ImageBase; namespace { - #define POWER_LAUNCHER_PID_SHARED_FILE L"Local\\3cbfbad4-199b-4e2c-9825-942d5d3d3c74" + const wchar_t POWER_LAUNCHER_PID_SHARED_FILE[] = L"Local\\3cbfbad4-199b-4e2c-9825-942d5d3d3c74"; + const wchar_t JSON_KEY_PROPERTIES[] = L"properties"; + const wchar_t JSON_KEY_WIN[] = L"win"; + const wchar_t JSON_KEY_ALT[] = L"alt"; + const wchar_t JSON_KEY_CTRL[] = L"ctrl"; + const wchar_t JSON_KEY_SHIFT[] = L"shift"; + const wchar_t JSON_KEY_CODE[] = L"code"; + const wchar_t JSON_KEY_OPEN_POWERLAUNCHER[] = L"open_powerlauncher"; } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) @@ -54,12 +62,27 @@ private: // Time to wait for process to close after sending WM_CLOSE signal static const int MAX_WAIT_MILLISEC = 10000; + // Hotkey to invoke the module + Hotkey m_hotkey = { .key = 0 }; + + // Helper function to extract the hotkey from the settings + void parse_hotkey(PowerToysSettings::PowerToyValues& settings); + + // Handle to event used to invoke the Runner + HANDLE m_hEvent; + public: // Constructor Microsoft_Launcher() { app_name = GET_RESOURCE_STRING(IDS_LAUNCHER_NAME); init_settings(); + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(sa); + sa.bInheritHandle = false; + sa.lpSecurityDescriptor = NULL; + m_hEvent = CreateEventW(&sa, FALSE, FALSE, CommonSharedConstants::POWER_LAUNCHER_SHARED_EVENT); }; ~Microsoft_Launcher() @@ -122,6 +145,7 @@ public: PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::from_json_string(config); + parse_hotkey(values); // If you don't need to do any custom processing of the settings, proceed // to persists the values calling: values.save_to_settings_file(); @@ -137,6 +161,7 @@ public: // Enable the powertoy virtual void enable() { + ResetEvent(m_hEvent); // Start PowerLauncher.exe only if the OS is 19H1 or higher if (UseNewSettings()) { @@ -144,8 +169,10 @@ public: if (!is_process_elevated(false)) { - std::wstring executable_args = L""; - executable_args.append(std::to_wstring(powertoys_pid)); + std::wstring executable_args; + executable_args += L" -powerToysPid "; + executable_args += std::to_wstring(powertoys_pid); + executable_args += L" --centralized-kb-hook"; SHELLEXECUTEINFOW sei{ sizeof(sei) }; sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; @@ -165,7 +192,8 @@ public: params += L"-target modules\\launcher\\PowerLauncher.exe "; params += L"-pidFile "; params += POWER_LAUNCHER_PID_SHARED_FILE; - params += L" " + std::to_wstring(powertoys_pid) + L" "; + params += L" -powerToysPid " + std::to_wstring(powertoys_pid) + L" "; + params += L"--centralized-kb-hook "; action_runner_path += L"\\action_runner.exe"; // Set up the shared file from which to retrieve the PID of PowerLauncher @@ -206,6 +234,7 @@ public: { if (m_enabled) { + ResetEvent(m_hEvent); terminateProcess(); } @@ -218,6 +247,37 @@ public: return m_enabled; } + // Return the invocation hotkey + virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override + { + if (m_hotkey.key) + { + if (hotkeys && buffer_size >= 1) + { + hotkeys[0] = m_hotkey; + } + + return 1; + } + else + { + return 0; + } + } + + // Process the hotkey event + virtual bool on_hotkey(size_t hotkeyId) override + { + // For now, hotkeyId will always be zero + if (m_enabled) + { + SetEvent(m_hEvent); + return true; + } + + return false; + } + // Callback to send WM_CLOSE signal to each top level window. static BOOL CALLBACK requestMainWindowClose(HWND nextWindow, LPARAM closePid) { @@ -251,6 +311,8 @@ void Microsoft_Launcher::init_settings() // Load and parse the settings file for this PowerToy. PowerToysSettings::PowerToyValues settings = PowerToysSettings::PowerToyValues::load_from_settings_file(get_name()); + + parse_hotkey(settings); } catch (std::exception ex) { @@ -258,6 +320,23 @@ void Microsoft_Launcher::init_settings() } } +void Microsoft_Launcher::parse_hotkey(PowerToysSettings::PowerToyValues& settings) +{ + try + { + auto jsonHotkeyObject = settings.get_raw_json().GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_OPEN_POWERLAUNCHER); + m_hotkey.win = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_WIN); + m_hotkey.alt = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_ALT); + m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT); + m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL); + m_hotkey.key = static_cast(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE)); + } + catch (...) + { + m_hotkey.key = 0; + } +} + extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() { return new Microsoft_Launcher(); diff --git a/src/modules/launcher/PowerLauncher/App.xaml.cs b/src/modules/launcher/PowerLauncher/App.xaml.cs index c8a1623c1f..64ae5c3b64 100644 --- a/src/modules/launcher/PowerLauncher/App.xaml.cs +++ b/src/modules/launcher/PowerLauncher/App.xaml.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; +using System.Linq; using System.Windows; using ManagedCommon; using Microsoft.PowerLauncher.Telemetry; @@ -30,7 +31,6 @@ namespace PowerLauncher private const string Unique = "PowerLauncher_Unique_Application_Mutex"; private static bool _disposed; - private static int _powerToysPid; private Settings _settings; private MainViewModel _mainVM; private MainWindow _mainWindow; @@ -40,15 +40,10 @@ namespace PowerLauncher private SettingsWatcher _settingsWatcher; [STAThread] - public static void Main(string[] args) + public static void Main() { if (SingleInstance.InitializeAsFirstInstance(Unique)) { - if (args?.Length > 0) - { - _ = int.TryParse(args[0], out _powerToysPid); - } - using (var application = new App()) { application.InitializeComponent(); @@ -59,17 +54,29 @@ namespace PowerLauncher private void OnStartup(object sender, StartupEventArgs e) { - RunnerHelper.WaitForPowerToysRunner(_powerToysPid, () => + for (int i = 0; i + 1 < e.Args.Length; i++) { - try + if (e.Args[i] == "-powerToysPid") { - Dispose(); + int powerToysPid; + if (int.TryParse(e.Args[i + 1], out powerToysPid)) + { + RunnerHelper.WaitForPowerToysRunner(powerToysPid, () => + { + try + { + Dispose(); + } + finally + { + Environment.Exit(0); + } + }); + } + + break; } - finally - { - Environment.Exit(0); - } - }); + } var bootTime = new System.Diagnostics.Stopwatch(); bootTime.Start(); @@ -85,6 +92,7 @@ namespace PowerLauncher _settingsVM = new SettingWindowViewModel(); _settings = _settingsVM.Settings; + _settings.UsePowerToysRunnerKeyboardHook = e.Args.Contains("--centralized-kb-hook"); _alphabet.Initialize(_settings); _stringMatcher = new StringMatcher(_alphabet); diff --git a/src/modules/launcher/PowerLauncher/Helper/NativeEventWaiter.cs b/src/modules/launcher/PowerLauncher/Helper/NativeEventWaiter.cs new file mode 100644 index 0000000000..6c9b27f7e6 --- /dev/null +++ b/src/modules/launcher/PowerLauncher/Helper/NativeEventWaiter.cs @@ -0,0 +1,28 @@ +// 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.Threading; +using System.Windows; + +namespace PowerLauncher.Helper +{ + public static class NativeEventWaiter + { + public static void WaitForEventLoop(string eventName, Action callback) + { + new Thread(() => + { + var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName); + while (true) + { + if (eventHandle.WaitOne()) + { + Application.Current.Dispatcher.Invoke(callback); + } + } + }).Start(); + } + } +} diff --git a/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs b/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs index 83d59804ac..028e39b3e6 100644 --- a/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs +++ b/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs @@ -55,7 +55,6 @@ namespace PowerLauncher.ViewModel public MainViewModel(Settings settings) { - HotkeyManager = new HotkeyManager(); _saved = false; _queryTextBeforeLeaveResults = string.Empty; _currentQuery = _emptyQuery; @@ -78,27 +77,36 @@ namespace PowerLauncher.ViewModel InitializeKeyCommands(); RegisterResultsUpdatedEvent(); - _settings.PropertyChanged += (s, e) => + if (settings != null && settings.UsePowerToysRunnerKeyboardHook) { - if (e.PropertyName == nameof(Settings.Hotkey)) + NativeEventWaiter.WaitForEventLoop(Constants.PowerLauncherSharedEvent(), OnHotkey); + _hotkeyHandle = 0; + } + else + { + HotkeyManager = new HotkeyManager(); + _settings.PropertyChanged += (s, e) => { - Application.Current.Dispatcher.Invoke(() => + if (e.PropertyName == nameof(Settings.Hotkey)) { - if (!string.IsNullOrEmpty(_settings.PreviousHotkey)) + Application.Current.Dispatcher.Invoke(() => { - HotkeyManager.UnregisterHotkey(_hotkeyHandle); - } + if (!string.IsNullOrEmpty(_settings.PreviousHotkey)) + { + HotkeyManager.UnregisterHotkey(_hotkeyHandle); + } - if (!string.IsNullOrEmpty(_settings.Hotkey)) - { - SetHotkey(_settings.Hotkey, OnHotkey); - } - }); - } - }; + if (!string.IsNullOrEmpty(_settings.Hotkey)) + { + SetHotkey(_settings.Hotkey, OnHotkey); + } + }); + } + }; - SetHotkey(_settings.Hotkey, OnHotkey); - SetCustomPluginHotkey(); + SetHotkey(_settings.Hotkey, OnHotkey); + SetCustomPluginHotkey(); + } } private void RegisterResultsUpdatedEvent() diff --git a/src/modules/launcher/Wox.Infrastructure/UserSettings/Settings.cs b/src/modules/launcher/Wox.Infrastructure/UserSettings/Settings.cs index 196705aa69..2d901d95e6 100644 --- a/src/modules/launcher/Wox.Infrastructure/UserSettings/Settings.cs +++ b/src/modules/launcher/Wox.Infrastructure/UserSettings/Settings.cs @@ -172,6 +172,8 @@ namespace Wox.Infrastructure.UserSettings public bool IgnoreHotkeysOnFullscreen { get; set; } + public bool UsePowerToysRunnerKeyboardHook { get; set; } + public HttpProxy Proxy { get; set; } = new HttpProxy(); [JsonConverter(typeof(StringEnumConverter))] diff --git a/src/runner/centralized_kb_hook.cpp b/src/runner/centralized_kb_hook.cpp new file mode 100644 index 0000000000..a9c2409b63 --- /dev/null +++ b/src/runner/centralized_kb_hook.cpp @@ -0,0 +1,131 @@ +#include "pch.h" +#include "centralized_kb_hook.h" +#include "common/common.h" + +namespace CentralizedKeyboardHook +{ + struct HotkeyDescriptor + { + Hotkey hotkey; + std::wstring moduleName; + std::function action; + + bool operator<(const HotkeyDescriptor& other) const + { + return hotkey < other.hotkey; + }; + + }; + + std::multiset hotkeyDescriptors; + std::mutex mutex; + HHOOK hHook{}; + + struct DestroyOnExit + { + ~DestroyOnExit() + { + Stop(); + } + } destroyOnExitObj; + + LRESULT CALLBACK KeyboardHookProc(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam) + { + if (nCode < 0 || ((wParam != WM_KEYDOWN) && (wParam != WM_SYSKEYDOWN))) + { + return CallNextHookEx(hHook, nCode, wParam, lParam); + } + + const auto& keyPressInfo = *reinterpret_cast(lParam); + + Hotkey hotkey{ + .win = (GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000), + .ctrl = static_cast(GetAsyncKeyState(VK_CONTROL) & 0x8000), + .shift = static_cast(GetAsyncKeyState(VK_SHIFT) & 0x8000), + .alt = static_cast(GetAsyncKeyState(VK_MENU) & 0x8000), + .key = static_cast(keyPressInfo.vkCode) + }; + + std::function action; + { + // Hold the lock for the shortest possible duration + std::unique_lock lock{ mutex }; + HotkeyDescriptor dummy{ .hotkey = hotkey }; + auto it = hotkeyDescriptors.find(dummy); + if (it != hotkeyDescriptors.end()) + { + action = it->action; + } + } + + if (action) + { + if (action()) + { + // After invoking the hotkey send a dummy key to prevent Start Menu from activating + INPUT dummyEvent[1] = {}; + dummyEvent[0].type = INPUT_KEYBOARD; + dummyEvent[0].ki.wVk = 0xFF; + dummyEvent[0].ki.dwFlags = KEYEVENTF_KEYUP; + SendInput(1, dummyEvent, sizeof(INPUT)); + + // Swallow the key press + return 1; + } + } + + return CallNextHookEx(hHook, nCode, wParam, lParam); + } + + void SetHotkeyAction(const std::wstring& moduleName, const Hotkey& hotkey, std::function&& action) noexcept + { + std::unique_lock lock{ mutex }; + hotkeyDescriptors.insert({ .hotkey = hotkey, .moduleName = moduleName, .action = std::move(action) }); + } + + void ClearModuleHotkeys(const std::wstring& moduleName) noexcept + { + std::unique_lock lock{ mutex }; + auto it = hotkeyDescriptors.begin(); + while (it != hotkeyDescriptors.end()) + { + if (it->moduleName == moduleName) + { + it = hotkeyDescriptors.erase(it); + } + else + { + ++it; + } + } + } + + void Start() noexcept + { +#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED) + const bool hook_disabled = IsDebuggerPresent(); +#else + const bool hook_disabled = false; +#endif + if (!hook_disabled) + { + if (!hHook) + { + hHook = SetWindowsHookExW(WH_KEYBOARD_LL, KeyboardHookProc, NULL, NULL); + if (!hHook) + { + DWORD errorCode = GetLastError(); + show_last_error_message(L"SetWindowsHookEx", errorCode, L"centralized_kb_hook"); + } + } + } + } + + void Stop() noexcept + { + if (hHook && UnhookWindowsHookEx(hHook)) + { + hHook = NULL; + } + } +} diff --git a/src/runner/centralized_kb_hook.h b/src/runner/centralized_kb_hook.h new file mode 100644 index 0000000000..89ad708353 --- /dev/null +++ b/src/runner/centralized_kb_hook.h @@ -0,0 +1,13 @@ +#include "pch.h" + +#include "../modules/interface/powertoy_module_interface.h" + +namespace CentralizedKeyboardHook +{ + using Hotkey = PowertoyModuleIface::Hotkey; + + void Start() noexcept; + void Stop() noexcept; + void SetHotkeyAction(const std::wstring& moduleName, const Hotkey& hotkey, std::function&& action) noexcept; + void ClearModuleHotkeys(const std::wstring& moduleName) noexcept; +}; diff --git a/src/runner/main.cpp b/src/runner/main.cpp index 9b1fe1fb44..cbc2b69a85 100644 --- a/src/runner/main.cpp +++ b/src/runner/main.cpp @@ -27,6 +27,7 @@ #include #include +#include "centralized_kb_hook.h" #if _DEBUG && _WIN64 #include "unhandled_exception_handler.h" @@ -89,6 +90,7 @@ int runner(bool isProcessElevated) #endif Trace::RegisterProvider(); start_tray_icon(); + CentralizedKeyboardHook::Start(); int result = -1; try diff --git a/src/runner/pch.h b/src/runner/pch.h index 98c9c46884..46810b960d 100644 --- a/src/runner/pch.h +++ b/src/runner/pch.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include diff --git a/src/runner/powertoy_module.cpp b/src/runner/powertoy_module.cpp index 23853cda07..ef2c45c853 100644 --- a/src/runner/powertoy_module.cpp +++ b/src/runner/powertoy_module.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "powertoy_module.h" +#include "centralized_kb_hook.h" std::map& modules() { @@ -42,4 +43,24 @@ PowertoyModule::PowertoyModule(PowertoyModuleIface* module, HMODULE handle) : { throw std::runtime_error("Module not initialized"); } + + update_hotkeys(); +} + +void PowertoyModule::update_hotkeys() +{ + CentralizedKeyboardHook::ClearModuleHotkeys(module->get_name()); + + size_t hotkeyCount = module->get_hotkeys(nullptr, 0); + std::vector hotkeys(hotkeyCount); + module->get_hotkeys(hotkeys.data(), hotkeyCount); + + auto modulePtr = module.get(); + + for (size_t i = 0; i < hotkeyCount; i++) + { + CentralizedKeyboardHook::SetHotkeyAction(module->get_name(), hotkeys[i], [modulePtr, i] { + return modulePtr->on_hotkey(i); + }); + } } diff --git a/src/runner/powertoy_module.h b/src/runner/powertoy_module.h index 36986d5053..20a9e93164 100644 --- a/src/runner/powertoy_module.h +++ b/src/runner/powertoy_module.h @@ -40,6 +40,8 @@ public: json::JsonObject json_config() const; + void update_hotkeys(); + private: std::unique_ptr handle; std::unique_ptr module; diff --git a/src/runner/runner.vcxproj b/src/runner/runner.vcxproj index dab20c641e..bf1a43a943 100644 --- a/src/runner/runner.vcxproj +++ b/src/runner/runner.vcxproj @@ -120,6 +120,7 @@ + @@ -132,6 +133,7 @@ + diff --git a/src/runner/runner.vcxproj.filters b/src/runner/runner.vcxproj.filters index 7354c01cd0..290a36c6b1 100644 --- a/src/runner/runner.vcxproj.filters +++ b/src/runner/runner.vcxproj.filters @@ -36,6 +36,9 @@ Utils + + Utils + @@ -73,7 +76,10 @@ Utils - Header Files + Utils + + + Utils diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp index 9417a3583d..71fb26b205 100644 --- a/src/runner/settings_window.cpp +++ b/src/runner/settings_window.cpp @@ -12,6 +12,7 @@ #include "common/common.h" #include "restart_elevated.h" #include "update_utils.h" +#include "centralized_kb_hook.h" #include #include @@ -106,9 +107,11 @@ std::optional dispatch_json_action_to_module(const json::JsonObjec void send_json_config_to_module(const std::wstring& module_key, const std::wstring& settings) { - if (modules().find(module_key) != modules().end()) + auto moduleIt = modules().find(module_key); + if (moduleIt != modules().end()) { - modules().at(module_key)->set_config(settings.c_str()); + moduleIt->second->set_config(settings.c_str()); + moduleIt->second.update_hotkeys(); } } From 6e89ef62e4cf9ff221e6526e97996e32b630dd1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Sto=C5=A1i=C4=87?= Date: Mon, 21 Sep 2020 13:51:30 +0200 Subject: [PATCH 02/22] [Launcher] Kill the Launcher immediately (#6747) * Kill the Launcher immediately * Also rename the shared file --- src/modules/launcher/Microsoft.Launcher/dllmain.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/modules/launcher/Microsoft.Launcher/dllmain.cpp b/src/modules/launcher/Microsoft.Launcher/dllmain.cpp index 45f46b41a0..c50c951a5e 100644 --- a/src/modules/launcher/Microsoft.Launcher/dllmain.cpp +++ b/src/modules/launcher/Microsoft.Launcher/dllmain.cpp @@ -11,7 +11,7 @@ extern "C" IMAGE_DOS_HEADER __ImageBase; namespace { - const wchar_t POWER_LAUNCHER_PID_SHARED_FILE[] = L"Local\\3cbfbad4-199b-4e2c-9825-942d5d3d3c74"; + const wchar_t POWER_LAUNCHER_PID_SHARED_FILE[] = L"Local\\PowerLauncherPidSharedFile-3cbfbad4-199b-4e2c-9825-942d5d3d3c74"; const wchar_t JSON_KEY_PROPERTIES[] = L"properties"; const wchar_t JSON_KEY_WIN[] = L"win"; const wchar_t JSON_KEY_ALT[] = L"alt"; @@ -294,12 +294,16 @@ public: void terminateProcess() { DWORD processID = GetProcessId(m_hProcess); + TerminateProcess(m_hProcess, 1); + // Temporarily disable sending a message to close + /* EnumWindows(&requestMainWindowClose, processID); const DWORD result = WaitForSingleObject(m_hProcess, MAX_WAIT_MILLISEC); if (result == WAIT_TIMEOUT || result == WAIT_FAILED) { TerminateProcess(m_hProcess, 1); } + */ } }; @@ -311,7 +315,7 @@ void Microsoft_Launcher::init_settings() // Load and parse the settings file for this PowerToy. PowerToysSettings::PowerToyValues settings = PowerToysSettings::PowerToyValues::load_from_settings_file(get_name()); - + parse_hotkey(settings); } catch (std::exception ex) @@ -335,7 +339,7 @@ void Microsoft_Launcher::parse_hotkey(PowerToysSettings::PowerToyValues& setting { m_hotkey.key = 0; } -} +} extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() { From 0f6428eed05d7da140e6f0e1437f7d2bb1510beb Mon Sep 17 00:00:00 2001 From: ryanbodrug-microsoft <56318517+ryanbodrug-microsoft@users.noreply.github.com> Date: Mon, 21 Sep 2020 10:14:44 -0700 Subject: [PATCH 03/22] User/ryanbod/mock settings disk access (#6188) * 1) Making Directory Methods private. 2) Removing the CreateDirectory / DeleteDirectory functionality from all Settings Unit Tests. * Abstracting disk access via IIOProvider to be able to provide mocks for unit tests instead of writing to disk. This also prevents developers who are running unit tests from interfering with the PowerToys settings on their local dev box. * Dependency Injecting stub SettingsUtils for all tests * Removing ISettingsUtils from constructors of objects that need to be deserialized (ColorPickerSettings/PowerLauncherSettings) as this breaks System.Text.Json * Removing unused namespace reference * Removing redifined mock * As per PR feedback. Stub Settings utils should work with any settings type if the intent is to compile / avoid null ref exceptions. Strangely when implementing this fix it became apparent that a stub settings isn't enough, and disk access needed to be mocked. I can't explain why the tests were passing previously. * Leveraging GetMockIOProviderForSaveLoadExists --- .../ColorPickerSettings.cs | 6 +- .../ISettingsUtils.cs | 17 ++++ .../PowerLauncherSettings.cs | 6 +- .../SettingsUtils.cs | 41 +++++---- .../Utilities/IIOProvider.cs | 21 +++++ .../Utilities/SystemIOProvider.cs | 42 +++++++++ .../ViewModels/ColorPickerViewModel.cs | 15 +-- .../ViewModels/FancyZonesViewModel.cs | 16 ++-- .../ViewModels/GeneralViewModel.cs | 15 +-- .../ViewModels/ImageResizerViewModel.cs | 35 ++++--- .../ViewModels/KeyboardManagerViewModel.cs | 20 ++-- .../ViewModels/PowerLauncherViewModel.cs | 17 ++-- .../ViewModels/PowerPreviewViewModel.cs | 10 +- .../ViewModels/PowerRenameViewModel.cs | 16 ++-- .../ViewModels/ShortcutGuideViewModel.cs | 16 ++-- ...oft.PowerToys.Settings.UI.UnitTests.csproj | 1 + .../Mocks/IIOProviderMocks.cs | 38 ++++++++ .../Mocks/ISettingsUtilsMocks.cs | 26 ++++++ .../ModelsTests/BasePTModuleSettingsTest.cs | 14 ++- .../ModelsTests/SettingsUtilsTests.cs | 65 ++++--------- .../ViewModelTests/ColorPicker.cs | 49 +--------- .../ViewModelTests/FancyZones.cs | 71 ++++----------- .../ViewModelTests/General.cs | 48 +++++----- .../ViewModelTests/ImageResizer.cs | 91 +++++++++---------- .../PowerLauncherViewModelTest.cs | 3 +- .../ViewModelTests/PowerPreview.cs | 40 +------- .../ViewModelTests/PowerRename.cs | 47 +++------- .../ViewModelTests/ShortcutGuide.cs | 46 ++-------- .../ModuleEnabledToForegroundConverter.cs | 5 +- .../Views/ColorPickerPage.xaml.cs | 5 +- .../Views/FancyZonesPage.xaml.cs | 5 +- .../Views/GeneralPage.xaml.cs | 4 + .../Views/ImageResizerPage.xaml.cs | 5 +- .../Views/KeyboardManagerPage.xaml.cs | 3 +- .../Views/PowerLauncherPage.xaml.cs | 5 +- .../Views/PowerPreviewPage.xaml.cs | 5 +- .../Views/PowerRenamePage.xaml.cs | 5 +- .../Views/ShortcutGuidePage.xaml.cs | 6 +- .../ColorPickerUI/Settings/UserSettings.cs | 8 +- .../launcher/PowerLauncher/SettingsWatcher.cs | 15 ++- 40 files changed, 468 insertions(+), 435 deletions(-) create mode 100644 src/core/Microsoft.PowerToys.Settings.UI.Lib/ISettingsUtils.cs create mode 100644 src/core/Microsoft.PowerToys.Settings.UI.Lib/Utilities/IIOProvider.cs create mode 100644 src/core/Microsoft.PowerToys.Settings.UI.Lib/Utilities/SystemIOProvider.cs create mode 100644 src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/IIOProviderMocks.cs create mode 100644 src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/ISettingsUtilsMocks.cs diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ColorPickerSettings.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ColorPickerSettings.cs index 382f6927fa..4e1a2be279 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ColorPickerSettings.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ColorPickerSettings.cs @@ -2,8 +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; using System.Text.Json; using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; namespace Microsoft.PowerToys.Settings.UI.Lib { @@ -21,7 +23,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib Name = ModuleName; } - public virtual void Save() + public virtual void Save(ISettingsUtils settingsUtils) { // Save settings to file var options = new JsonSerializerOptions @@ -29,7 +31,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib WriteIndented = true, }; - SettingsUtils.SaveSettings(JsonSerializer.Serialize(this, options), ModuleName); + settingsUtils.SaveSettings(JsonSerializer.Serialize(this, options), ModuleName); } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ISettingsUtils.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ISettingsUtils.cs new file mode 100644 index 0000000000..3430b0b18c --- /dev/null +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ISettingsUtils.cs @@ -0,0 +1,17 @@ +// 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.PowerToys.Settings.UI.Lib +{ + public interface ISettingsUtils + { + T GetSettings(string powertoy = "", string fileName = "settings.json"); + + void SaveSettings(string jsonSettings, string powertoy = "", string fileName = "settings.json"); + + bool SettingsExists(string powertoy = "", string fileName = "settings.json"); + + void DeleteSettings(string powertoy = ""); + } +} diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherSettings.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherSettings.cs index 9b73b878eb..7eddad108f 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherSettings.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherSettings.cs @@ -2,8 +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; using System.Text.Json; using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; namespace Microsoft.PowerToys.Settings.UI.Lib { @@ -21,7 +23,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib Name = ModuleName; } - public virtual void Save() + public virtual void Save(ISettingsUtils settingsUtils) { // Save settings to file var options = new JsonSerializerOptions @@ -29,7 +31,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib WriteIndented = true, }; - SettingsUtils.SaveSettings(JsonSerializer.Serialize(this, options), ModuleName); + settingsUtils.SaveSettings(JsonSerializer.Serialize(this, options), ModuleName); } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/SettingsUtils.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/SettingsUtils.cs index c69883369b..b0096bbb9c 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/SettingsUtils.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/SettingsUtils.cs @@ -3,30 +3,35 @@ // See the LICENSE file in the project root for more information. using System; -using System.IO; -using System.Runtime.Serialization; using System.Text.Json; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; namespace Microsoft.PowerToys.Settings.UI.Lib { - public static class SettingsUtils + public class SettingsUtils : ISettingsUtils { private const string DefaultFileName = "settings.json"; private const string DefaultModuleName = ""; + private IIOProvider _ioProvider; - public static void DeleteSettings(string powertoy, string fileName = DefaultFileName) + public SettingsUtils(IIOProvider ioProvider) { - File.Delete(GetSettingsPath(powertoy, fileName)); + _ioProvider = ioProvider ?? throw new ArgumentNullException(nameof(ioProvider)); } - public static bool SettingsFolderExists(string powertoy) + private bool SettingsFolderExists(string powertoy) { - return Directory.Exists(Path.Combine(LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}")); + return _ioProvider.DirectoryExists(System.IO.Path.Combine(LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}")); } - public static void CreateSettingsFolder(string powertoy) + private void CreateSettingsFolder(string powertoy) { - Directory.CreateDirectory(Path.Combine(LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}")); + _ioProvider.CreateDirectory(System.IO.Path.Combine(LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}")); + } + + public void DeleteSettings(string powertoy = "") + { + _ioProvider.DeleteDirectory(System.IO.Path.Combine(LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}")); } /// @@ -37,38 +42,38 @@ namespace Microsoft.PowerToys.Settings.UI.Lib { if (string.IsNullOrWhiteSpace(powertoy)) { - return Path.Combine( + return System.IO.Path.Combine( LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{fileName}"); } - return Path.Combine( + return System.IO.Path.Combine( LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}\\{fileName}"); } - public static bool SettingsExists(string powertoy = DefaultModuleName, string fileName = DefaultFileName) + public bool SettingsExists(string powertoy = DefaultModuleName, string fileName = DefaultFileName) { - return File.Exists(GetSettingsPath(powertoy, fileName)); + return _ioProvider.FileExists(GetSettingsPath(powertoy, fileName)); } /// /// Get a Deserialized object of the json settings string. /// /// Deserialized json settings object. - public static T GetSettings(string powertoy = DefaultModuleName, string fileName = DefaultFileName) + public T GetSettings(string powertoy = DefaultModuleName, string fileName = DefaultFileName) { // Adding Trim('\0') to overcome possible NTFS file corruption. // Look at issue https://github.com/microsoft/PowerToys/issues/6413 you'll see the file has a large sum of \0 to fill up a 4096 byte buffer for writing to disk // This, while not totally ideal, does work around the problem by trimming the end. // The file itself did write the content correctly but something is off with the actual end of the file, hence the 0x00 bug - var jsonSettingsString = File.ReadAllText(GetSettingsPath(powertoy, fileName)).Trim('\0'); + var jsonSettingsString = _ioProvider.ReadAllText(GetSettingsPath(powertoy, fileName)).Trim('\0'); return JsonSerializer.Deserialize(jsonSettingsString); } // Save settings to a json file. - public static void SaveSettings(string jsonSettings, string powertoy = DefaultModuleName, string fileName = DefaultFileName) + public void SaveSettings(string jsonSettings, string powertoy = DefaultModuleName, string fileName = DefaultFileName) { try { @@ -79,7 +84,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib CreateSettingsFolder(powertoy); } - File.WriteAllText(GetSettingsPath(powertoy, fileName), jsonSettings); + _ioProvider.WriteAllText(GetSettingsPath(powertoy, fileName), jsonSettings); } } catch @@ -87,7 +92,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib } } - public static string LocalApplicationDataFolder() + private static string LocalApplicationDataFolder() { return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/Utilities/IIOProvider.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/Utilities/IIOProvider.cs new file mode 100644 index 0000000000..deed5f0fcd --- /dev/null +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/Utilities/IIOProvider.cs @@ -0,0 +1,21 @@ +// 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.PowerToys.Settings.UI.Lib.Utilities +{ + public interface IIOProvider + { + bool FileExists(string path); + + bool DirectoryExists(string path); + + bool CreateDirectory(string path); + + void DeleteDirectory(string path); + + void WriteAllText(string path, string content); + + string ReadAllText(string path); + } +} diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/Utilities/SystemIOProvider.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/Utilities/SystemIOProvider.cs new file mode 100644 index 0000000000..be01a3578c --- /dev/null +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/Utilities/SystemIOProvider.cs @@ -0,0 +1,42 @@ +// 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.IO; + +namespace Microsoft.PowerToys.Settings.UI.Lib.Utilities +{ + public class SystemIOProvider : IIOProvider + { + public bool CreateDirectory(string path) + { + var directioryInfo = Directory.CreateDirectory(path); + return directioryInfo != null; + } + + public void DeleteDirectory(string path) + { + Directory.Delete(path); + } + + public bool DirectoryExists(string path) + { + return Directory.Exists(path); + } + + public bool FileExists(string path) + { + return File.Exists(path); + } + + public string ReadAllText(string path) + { + return File.ReadAllText(path); + } + + public void WriteAllText(string path, string content) + { + File.WriteAllText(path, content); + } + } +} diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ColorPickerViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ColorPickerViewModel.cs index e3ccd66207..6d49d42355 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ColorPickerViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ColorPickerViewModel.cs @@ -5,30 +5,33 @@ using System; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class ColorPickerViewModel : Observable { + private readonly ISettingsUtils _settingsUtils; private ColorPickerSettings _colorPickerSettings; private bool _isEnabled; private Func SendConfigMSG { get; } - public ColorPickerViewModel(Func ipcMSGCallBackFunc) + public ColorPickerViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc) { - if (SettingsUtils.SettingsExists(ColorPickerSettings.ModuleName)) + _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); + if (_settingsUtils.SettingsExists(ColorPickerSettings.ModuleName)) { - _colorPickerSettings = SettingsUtils.GetSettings(ColorPickerSettings.ModuleName); + _colorPickerSettings = _settingsUtils.GetSettings(ColorPickerSettings.ModuleName); } else { _colorPickerSettings = new ColorPickerSettings(); } - if (SettingsUtils.SettingsExists()) + if (_settingsUtils.SettingsExists()) { - var generalSettings = SettingsUtils.GetSettings(); + var generalSettings = _settingsUtils.GetSettings(); _isEnabled = generalSettings.Enabled.ColorPicker; } @@ -51,7 +54,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels OnPropertyChanged(nameof(IsEnabled)); // grab the latest version of settings - var generalSettings = SettingsUtils.GetSettings(); + var generalSettings = _settingsUtils.GetSettings(); generalSettings.Enabled.ColorPicker = value; OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(generalSettings); SendConfigMSG(outgoing.ToString()); diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/FancyZonesViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/FancyZonesViewModel.cs index 3b538c2c66..46929bb2a5 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/FancyZonesViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/FancyZonesViewModel.cs @@ -6,12 +6,15 @@ using System; using System.Drawing; using System.Runtime.CompilerServices; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels.Commands; namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class FancyZonesViewModel : Observable { + private readonly ISettingsUtils _settingsUtils; + private const string ModuleName = "FancyZones"; public ButtonClickCommand LaunchEditorEventHandler { get; set; } @@ -22,18 +25,19 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels private string settingsConfigFileFolder = string.Empty; - public FancyZonesViewModel(Func ipcMSGCallBackFunc, string configFileSubfolder = "") + public FancyZonesViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc, string configFileSubfolder = "") { settingsConfigFileFolder = configFileSubfolder; + _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); try { - Settings = SettingsUtils.GetSettings(GetSettingsSubPath()); + Settings = _settingsUtils.GetSettings(GetSettingsSubPath()); } catch { Settings = new FancyZonesSettings(); - SettingsUtils.SaveSettings(Settings.ToJsonString(), GetSettingsSubPath()); + _settingsUtils.SaveSettings(Settings.ToJsonString(), GetSettingsSubPath()); } LaunchEditorEventHandler = new ButtonClickCommand(LaunchEditor); @@ -71,12 +75,12 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels GeneralSettings generalSettings; try { - generalSettings = SettingsUtils.GetSettings(string.Empty); + generalSettings = _settingsUtils.GetSettings(string.Empty); } catch { generalSettings = new GeneralSettings(); - SettingsUtils.SaveSettings(generalSettings.ToJsonString(), string.Empty); + _settingsUtils.SaveSettings(generalSettings.ToJsonString(), string.Empty); } _isEnabled = generalSettings.Enabled.FancyZones; @@ -117,7 +121,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels if (value != _isEnabled) { _isEnabled = value; - GeneralSettings generalSettings = SettingsUtils.GetSettings(string.Empty); + GeneralSettings generalSettings = _settingsUtils.GetSettings(string.Empty); generalSettings.Enabled.FancyZones = value; OutGoingGeneralSettings snd = new OutGoingGeneralSettings(generalSettings); diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/GeneralViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/GeneralViewModel.cs index 19e6ce3d6a..19864f120c 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/GeneralViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/GeneralViewModel.cs @@ -13,6 +13,8 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class GeneralViewModel : Observable { + private readonly ISettingsUtils _settingsUtils; + private GeneralSettings GeneralSettingsConfigs { get; set; } public ButtonClickCommand CheckFoUpdatesEventHandler { get; set; } @@ -33,20 +35,21 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels private string _settingsConfigFileFolder = string.Empty; - public GeneralViewModel(string runAsAdminText, string runAsUserText, bool isElevated, bool isAdmin, Func updateTheme, Func ipcMSGCallBackFunc, Func ipcMSGRestartAsAdminMSGCallBackFunc, Func ipcMSGCheckForUpdatesCallBackFunc, string configFileSubfolder = "") + public GeneralViewModel(ISettingsUtils settingsUtils, string runAsAdminText, string runAsUserText, bool isElevated, bool isAdmin, Func updateTheme, Func ipcMSGCallBackFunc, Func ipcMSGRestartAsAdminMSGCallBackFunc, Func ipcMSGCheckForUpdatesCallBackFunc, string configFileSubfolder = "") { CheckFoUpdatesEventHandler = new ButtonClickCommand(CheckForUpdates_Click); RestartElevatedButtonEventHandler = new ButtonClickCommand(Restart_Elevated); + _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); try { - GeneralSettingsConfigs = SettingsUtils.GetSettings(string.Empty); + GeneralSettingsConfigs = _settingsUtils.GetSettings(string.Empty); if (Helper.CompareVersions(GeneralSettingsConfigs.PowertoysVersion, Helper.GetProductVersion()) < 0) { // Update settings GeneralSettingsConfigs.PowertoysVersion = Helper.GetProductVersion(); - SettingsUtils.SaveSettings(GeneralSettingsConfigs.ToJsonString(), string.Empty); + _settingsUtils.SaveSettings(GeneralSettingsConfigs.ToJsonString(), string.Empty); } } catch (FormatException e) @@ -57,7 +60,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels catch { GeneralSettingsConfigs = new GeneralSettings(); - SettingsUtils.SaveSettings(GeneralSettingsConfigs.ToJsonString(), string.Empty); + _settingsUtils.SaveSettings(GeneralSettingsConfigs.ToJsonString(), string.Empty); } // set the callback functions value to hangle outgoing IPC message. @@ -360,7 +363,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels // callback function to launch the URL to check for updates. private void CheckForUpdates_Click() { - GeneralSettings settings = SettingsUtils.GetSettings(_settingsConfigFileFolder); + GeneralSettings settings = _settingsUtils.GetSettings(_settingsConfigFileFolder); settings.CustomActionName = "check_for_updates"; OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(settings); @@ -371,7 +374,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels public void Restart_Elevated() { - GeneralSettings settings = SettingsUtils.GetSettings(_settingsConfigFileFolder); + GeneralSettings settings = _settingsUtils.GetSettings(_settingsConfigFileFolder); settings.CustomActionName = "restart_elevation"; OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(settings); diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ImageResizerViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ImageResizerViewModel.cs index 373f7d0f55..5110092799 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ImageResizerViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ImageResizerViewModel.cs @@ -7,39 +7,44 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class ImageResizerViewModel : Observable { + private readonly ISettingsUtils _settingsUtils; + private ImageResizerSettings Settings { get; set; } private const string ModuleName = "ImageResizer"; private Func SendConfigMSG { get; } - public ImageResizerViewModel(Func ipcMSGCallBackFunc) + public ImageResizerViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc) { + _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); + try { - Settings = SettingsUtils.GetSettings(ModuleName); + Settings = _settingsUtils.GetSettings(ModuleName); } catch { Settings = new ImageResizerSettings(); - SettingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); + _settingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); } GeneralSettings generalSettings; try { - generalSettings = SettingsUtils.GetSettings(string.Empty); + generalSettings = _settingsUtils.GetSettings(string.Empty); } catch { generalSettings = new GeneralSettings(); - SettingsUtils.SaveSettings(generalSettings.ToJsonString(), string.Empty); + _settingsUtils.SaveSettings(generalSettings.ToJsonString(), string.Empty); } // set the callback functions value to hangle outgoing IPC message. @@ -84,7 +89,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels if (value != _isEnabled) { _isEnabled = value; - GeneralSettings generalSettings = SettingsUtils.GetSettings(string.Empty); + GeneralSettings generalSettings = _settingsUtils.GetSettings(string.Empty); generalSettings.Enabled.ImageResizer = value; OutGoingGeneralSettings snd = new OutGoingGeneralSettings(generalSettings); SendConfigMSG(snd.ToString()); @@ -121,7 +126,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { _jpegQualityLevel = value; Settings.Properties.ImageresizerJpegQualityLevel.Value = value; - SettingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); + _settingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); OnPropertyChanged("JPEGQualityLevel"); } } @@ -140,7 +145,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { _pngInterlaceOption = value; Settings.Properties.ImageresizerPngInterlaceOption.Value = value; - SettingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); + _settingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); OnPropertyChanged("PngInterlaceOption"); } } @@ -159,7 +164,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { _tiffCompressOption = value; Settings.Properties.ImageresizerTiffCompressOption.Value = value; - SettingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); + _settingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); OnPropertyChanged("TiffCompressOption"); } } @@ -178,7 +183,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { _fileName = value; Settings.Properties.ImageresizerFileName.Value = value; - SettingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); + _settingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); OnPropertyChanged("FileName"); } } @@ -195,7 +200,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { _keepDateModified = value; Settings.Properties.ImageresizerKeepDateModified.Value = value; - SettingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); + _settingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); OnPropertyChanged("KeepDateModified"); } } @@ -212,9 +217,9 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels if (_encoderGuidId != value) { _encoderGuidId = value; - SettingsUtils.SaveSettings(Settings.Properties.ImageresizerSizes.ToJsonString(), ModuleName, "sizes.json"); + _settingsUtils.SaveSettings(Settings.Properties.ImageresizerSizes.ToJsonString(), ModuleName, "sizes.json"); Settings.Properties.ImageresizerFallbackEncoder.Value = GetEncoderGuid(value); - SettingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); + _settingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); OnPropertyChanged("Encoder"); } } @@ -243,9 +248,9 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels public void SavesImageSizes(ObservableCollection imageSizes) { - SettingsUtils.SaveSettings(Settings.Properties.ImageresizerSizes.ToJsonString(), ModuleName, "sizes.json"); + _settingsUtils.SaveSettings(Settings.Properties.ImageresizerSizes.ToJsonString(), ModuleName, "sizes.json"); Settings.Properties.ImageresizerSizes = new ImageResizerSizes(imageSizes); - SettingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); + _settingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); } public string GetEncoderGuid(int value) diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/KeyboardManagerViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/KeyboardManagerViewModel.cs index 712696733d..7f2df05c49 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/KeyboardManagerViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/KeyboardManagerViewModel.cs @@ -16,6 +16,8 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class KeyboardManagerViewModel : Observable { + private readonly ISettingsUtils _settingsUtils; + private const string PowerToyName = "Keyboard Manager"; private const string RemapKeyboardActionName = "RemapKeyboard"; private const string RemapKeyboardActionValue = "Open Remap Keyboard Window"; @@ -36,16 +38,18 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels private Func, int> FilterRemapKeysList { get; } - public KeyboardManagerViewModel(Func ipcMSGCallBackFunc, Func, int> filterRemapKeysList) + public KeyboardManagerViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc, Func, int> filterRemapKeysList) { // set the callback functions value to hangle outgoing IPC message. SendConfigMSG = ipcMSGCallBackFunc; FilterRemapKeysList = filterRemapKeysList; - if (SettingsUtils.SettingsExists(PowerToyName)) + _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); + + if (_settingsUtils.SettingsExists(PowerToyName)) { // Todo: Be more resilient while reading and saving settings. - Settings = SettingsUtils.GetSettings(PowerToyName); + Settings = _settingsUtils.GetSettings(PowerToyName); // Load profile. if (!LoadProfile()) @@ -56,17 +60,17 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels else { Settings = new KeyboardManagerSettings(PowerToyName); - SettingsUtils.SaveSettings(Settings.ToJsonString(), PowerToyName); + _settingsUtils.SaveSettings(Settings.ToJsonString(), PowerToyName); } - if (SettingsUtils.SettingsExists()) + if (_settingsUtils.SettingsExists()) { - _generalSettings = SettingsUtils.GetSettings(string.Empty); + _generalSettings = _settingsUtils.GetSettings(string.Empty); } else { _generalSettings = new GeneralSettings(); - SettingsUtils.SaveSettings(_generalSettings.ToJsonString(), string.Empty); + _settingsUtils.SaveSettings(_generalSettings.ToJsonString(), string.Empty); } } @@ -173,7 +177,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels // update the UI element here. try { - _profile = SettingsUtils.GetSettings(PowerToyName, Settings.Properties.ActiveConfiguration.Value + JsonFileType); + _profile = _settingsUtils.GetSettings(PowerToyName, Settings.Properties.ActiveConfiguration.Value + JsonFileType); FilterRemapKeysList(_profile.RemapKeys.InProcessRemapKeys); } finally diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerLauncherViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerLauncherViewModel.cs index fbccc433fe..73e15ae558 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerLauncherViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerLauncherViewModel.cs @@ -6,11 +6,14 @@ using System; using System.Runtime.CompilerServices; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class PowerLauncherViewModel : Observable { + private readonly ISettingsUtils _settingsUtils; + private PowerLauncherSettings settings; private GeneralSettings generalSettings; @@ -20,21 +23,21 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels private Func SendConfigMSG { get; } - public PowerLauncherViewModel(Func ipcMSGCallBackFunc, int defaultKeyCode) + public PowerLauncherViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc, int defaultKeyCode) { + _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); + // set the callback functions value to hangle outgoing IPC message. SendConfigMSG = ipcMSGCallBackFunc; - callback = (PowerLauncherSettings settings) => { // Propagate changes to Power Launcher through IPC SendConfigMSG( string.Format("{{ \"powertoys\": {{ \"{0}\": {1} }} }}", PowerLauncherSettings.ModuleName, JsonSerializer.Serialize(settings))); }; - - if (SettingsUtils.SettingsExists(PowerLauncherSettings.ModuleName)) + if (_settingsUtils.SettingsExists(PowerLauncherSettings.ModuleName)) { - settings = SettingsUtils.GetSettings(PowerLauncherSettings.ModuleName); + settings = _settingsUtils.GetSettings(PowerLauncherSettings.ModuleName); } else { @@ -45,9 +48,9 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels callback(settings); } - if (SettingsUtils.SettingsExists()) + if (_settingsUtils.SettingsExists()) { - generalSettings = SettingsUtils.GetSettings(); + generalSettings = _settingsUtils.GetSettings(); } else { diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerPreviewViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerPreviewViewModel.cs index e79746f2fe..7415c8eb65 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerPreviewViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerPreviewViewModel.cs @@ -5,11 +5,14 @@ using System; using System.Runtime.CompilerServices; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class PowerPreviewViewModel : Observable { + private readonly ISettingsUtils _settingsUtils; + private const string ModuleName = "File Explorer"; private PowerPreviewSettings Settings { get; set; } @@ -18,19 +21,20 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels private string _settingsConfigFileFolder = string.Empty; - public PowerPreviewViewModel(Func ipcMSGCallBackFunc, string configFileSubfolder = "") + public PowerPreviewViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc, string configFileSubfolder = "") { // Update Settings file folder: _settingsConfigFileFolder = configFileSubfolder; + _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); try { - Settings = SettingsUtils.GetSettings(GetSettingsSubPath()); + Settings = _settingsUtils.GetSettings(GetSettingsSubPath()); } catch { Settings = new PowerPreviewSettings(); - SettingsUtils.SaveSettings(Settings.ToJsonString(), GetSettingsSubPath()); + _settingsUtils.SaveSettings(Settings.ToJsonString(), GetSettingsSubPath()); } // set the callback functions value to hangle outgoing IPC message. diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerRenameViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerRenameViewModel.cs index 43755e6914..727dfd676b 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerRenameViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerRenameViewModel.cs @@ -5,11 +5,14 @@ using System; using System.Runtime.CompilerServices; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class PowerRenameViewModel : Observable { + private readonly ISettingsUtils _settingsUtils; + private const string ModuleName = "PowerRename"; private string _settingsConfigFileFolder = string.Empty; @@ -18,21 +21,22 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels private Func SendConfigMSG { get; } - public PowerRenameViewModel(Func ipcMSGCallBackFunc, string configFileSubfolder = "") + public PowerRenameViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc, string configFileSubfolder = "") { // Update Settings file folder: _settingsConfigFileFolder = configFileSubfolder; + _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); try { - PowerRenameLocalProperties localSettings = SettingsUtils.GetSettings(GetSettingsSubPath(), "power-rename-settings.json"); + PowerRenameLocalProperties localSettings = _settingsUtils.GetSettings(GetSettingsSubPath(), "power-rename-settings.json"); Settings = new PowerRenameSettings(localSettings); } catch { PowerRenameLocalProperties localSettings = new PowerRenameLocalProperties(); Settings = new PowerRenameSettings(localSettings); - SettingsUtils.SaveSettings(localSettings.ToJsonString(), GetSettingsSubPath(), "power-rename-settings.json"); + _settingsUtils.SaveSettings(localSettings.ToJsonString(), GetSettingsSubPath(), "power-rename-settings.json"); } // set the callback functions value to hangle outgoing IPC message. @@ -47,12 +51,12 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels GeneralSettings generalSettings; try { - generalSettings = SettingsUtils.GetSettings(string.Empty); + generalSettings = _settingsUtils.GetSettings(string.Empty); } catch { generalSettings = new GeneralSettings(); - SettingsUtils.SaveSettings(generalSettings.ToJsonString(), string.Empty); + _settingsUtils.SaveSettings(generalSettings.ToJsonString(), string.Empty); } _powerRenameEnabled = generalSettings.Enabled.PowerRename; @@ -76,7 +80,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { if (value != _powerRenameEnabled) { - GeneralSettings generalSettings = SettingsUtils.GetSettings(string.Empty); + GeneralSettings generalSettings = _settingsUtils.GetSettings(string.Empty); generalSettings.Enabled.PowerRename = value; OutGoingGeneralSettings snd = new OutGoingGeneralSettings(generalSettings); SendConfigMSG(snd.ToString()); diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ShortcutGuideViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ShortcutGuideViewModel.cs index 32ed136f5b..6c4f5d645e 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ShortcutGuideViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ShortcutGuideViewModel.cs @@ -5,11 +5,14 @@ using System; using System.Runtime.CompilerServices; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class ShortcutGuideViewModel : Observable { + private readonly ISettingsUtils _settingsUtils; + private ShortcutGuideSettings Settings { get; set; } private const string ModuleName = "Shortcut Guide"; @@ -18,31 +21,32 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels private string _settingsConfigFileFolder = string.Empty; - public ShortcutGuideViewModel(Func ipcMSGCallBackFunc, string configFileSubfolder = "") + public ShortcutGuideViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc, string configFileSubfolder = "") { // Update Settings file folder: _settingsConfigFileFolder = configFileSubfolder; + _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); try { - Settings = SettingsUtils.GetSettings(GetSettingsSubPath()); + Settings = _settingsUtils.GetSettings(GetSettingsSubPath()); } catch { Settings = new ShortcutGuideSettings(); - SettingsUtils.SaveSettings(Settings.ToJsonString(), GetSettingsSubPath()); + _settingsUtils.SaveSettings(Settings.ToJsonString(), GetSettingsSubPath()); } GeneralSettings generalSettings; try { - generalSettings = SettingsUtils.GetSettings(string.Empty); + generalSettings = _settingsUtils.GetSettings(string.Empty); } catch { generalSettings = new GeneralSettings(); - SettingsUtils.SaveSettings(generalSettings.ToJsonString(), string.Empty); + _settingsUtils.SaveSettings(generalSettings.ToJsonString(), string.Empty); } // set the callback functions value to hangle outgoing IPC message. @@ -87,7 +91,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels if (value != _isEnabled) { _isEnabled = value; - GeneralSettings generalSettings = SettingsUtils.GetSettings(string.Empty); + GeneralSettings generalSettings = _settingsUtils.GetSettings(string.Empty); generalSettings.Enabled.ShortcutGuide = value; OutGoingGeneralSettings snd = new OutGoingGeneralSettings(generalSettings); SendConfigMSG(snd.ToString()); diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Microsoft.PowerToys.Settings.UI.UnitTests.csproj b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Microsoft.PowerToys.Settings.UI.UnitTests.csproj index 525ce3d286..42214bf52e 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Microsoft.PowerToys.Settings.UI.UnitTests.csproj +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Microsoft.PowerToys.Settings.UI.UnitTests.csproj @@ -17,6 +17,7 @@ + diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/IIOProviderMocks.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/IIOProviderMocks.cs new file mode 100644 index 0000000000..08a6b02977 --- /dev/null +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/IIOProviderMocks.cs @@ -0,0 +1,38 @@ +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; +using Moq; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.PowerToys.Settings.UI.UnitTests.Mocks +{ + internal static class IIOProviderMocks + { + + /// + /// This method mocks an IO provider to validate tests wich required saving to a file, and then reading the contents of that file, or verifying it exists + /// + /// + internal static Mock GetMockIOProviderForSaveLoadExists() + { + string savePath = string.Empty; + string saveContent = string.Empty; + var mockIOProvider = new Mock(); + mockIOProvider.Setup(x => x.WriteAllText(It.IsAny(), It.IsAny())) + .Callback((path, content) => + { + savePath = path; + saveContent = content; + }); + mockIOProvider.Setup(x => x.ReadAllText(It.Is(x => x.Equals(savePath, StringComparison.Ordinal)))) + .Returns(() => saveContent); + + mockIOProvider.Setup(x => x.FileExists(It.Is(x => x.Equals(savePath, StringComparison.Ordinal)))) + .Returns(true); + mockIOProvider.Setup(x => x.FileExists(It.Is(x => !x.Equals(savePath, StringComparison.Ordinal)))) + .Returns(false); + + return mockIOProvider; + } + } +} diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/ISettingsUtilsMocks.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/ISettingsUtilsMocks.cs new file mode 100644 index 0000000000..e1e200ce96 --- /dev/null +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/ISettingsUtilsMocks.cs @@ -0,0 +1,26 @@ +using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; +using Moq; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.PowerToys.Settings.UI.UnitTests.Mocks +{ + internal static class ISettingsUtilsMocks + { + //Stubs out empty values for imageresizersettings and general settings as needed by the imageresizer viewmodel + internal static Mock GetStubSettingsUtils() + { + var settingsUtils = new Mock(); + settingsUtils.Setup(x => x.GetSettings(It.IsAny(), It.IsAny())) + .Returns(new InvocationFunc(invocation => + { + var typeArgument = invocation.Method.GetGenericArguments()[0]; + return Activator.CreateInstance(typeArgument); + })); + + return settingsUtils; + } + } +} diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/BasePTModuleSettingsTest.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/BasePTModuleSettingsTest.cs index 84bba538c8..cc4a2d158c 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/BasePTModuleSettingsTest.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/BasePTModuleSettingsTest.cs @@ -4,8 +4,11 @@ using System; using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; +using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.PowerToys.Settings.UnitTest; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Schema; @@ -21,6 +24,13 @@ namespace CommonLibTest [Obsolete] public void ToJsonString_ShouldReturnValidJSONOfModel_WhenSuccessful() { + //Mock Disk access + string saveContent = string.Empty; + string savePath = string.Empty; + var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); + + var settingsUtils = new SettingsUtils(mockIOProvider.Object); + // Arrange string file_name = "test\\BasePTModuleSettingsTest"; string expectedSchemaText = @" @@ -39,11 +49,11 @@ namespace CommonLibTest }"; string testSettingsConfigs = new BasePTSettingsTest().ToJsonString(); - SettingsUtils.SaveSettings(testSettingsConfigs, file_name); + settingsUtils.SaveSettings(testSettingsConfigs, file_name); JsonSchema expectedSchema = JsonSchema.Parse(expectedSchemaText); // Act - JObject actualSchema = JObject.Parse(SettingsUtils.GetSettings(file_name).ToJsonString()); + JObject actualSchema = JObject.Parse(settingsUtils.GetSettings(file_name).ToJsonString()); bool valid = actualSchema.IsValid(expectedSchema); // Assert diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs index 1122c3845d..36a4165407 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs @@ -7,45 +7,34 @@ using System.IO; using System.Linq; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; +using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.PowerToys.Settings.UnitTest; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; namespace CommonLibTest { [TestClass] public class SettingsUtilsTests { - public SettingsUtilsTests() - { - string file_name = "\\test"; - if (SettingsUtils.SettingsFolderExists(file_name)) - { - DeleteFolder(file_name); - } - } - [TestCleanup] - public void Cleanup() - { - string file_name = "\\test"; - if (SettingsUtils.SettingsFolderExists(file_name)) - { - DeleteFolder(file_name); - } - } [TestMethod] public void SaveSettings_SaveSettingsToFile_WhenFilePathExists() { // Arrange + var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); + var settingsUtils = new SettingsUtils(mockIOProvider.Object); + string file_name = "\\test"; string file_contents_correct_json_content = "{\"name\":\"powertoy module name\",\"version\":\"powertoy version\"}"; BasePTSettingsTest expected_json = JsonSerializer.Deserialize(file_contents_correct_json_content); // Act - SettingsUtils.SaveSettings(file_contents_correct_json_content, file_name); - BasePTSettingsTest actual_json = SettingsUtils.GetSettings(file_name); + settingsUtils.SaveSettings(file_contents_correct_json_content, file_name); + BasePTSettingsTest actual_json = settingsUtils.GetSettings(file_name); // Assert Assert.AreEqual(expected_json.ToJsonString(), actual_json.ToJsonString()); @@ -55,19 +44,15 @@ namespace CommonLibTest public void SaveSettings_ShouldCreateFile_WhenFilePathIsNotFound() { // Arrange + var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); + var settingsUtils = new SettingsUtils(mockIOProvider.Object); string file_name = "test\\Test Folder"; string file_contents_correct_json_content = "{\"name\":\"powertoy module name\",\"version\":\"powertoy version\"}"; BasePTSettingsTest expected_json = JsonSerializer.Deserialize(file_contents_correct_json_content); - // Act - if (SettingsUtils.SettingsFolderExists(file_name)) - { - DeleteFolder(file_name); - } - - SettingsUtils.SaveSettings(file_contents_correct_json_content, file_name); - BasePTSettingsTest actual_json = SettingsUtils.GetSettings(file_name); + settingsUtils.SaveSettings(file_contents_correct_json_content, file_name); + BasePTSettingsTest actual_json = settingsUtils.GetSettings(file_name); // Assert Assert.AreEqual(expected_json.ToJsonString(), actual_json.ToJsonString()); @@ -77,39 +62,23 @@ namespace CommonLibTest public void SettingsFolderExists_ShouldReturnFalse_WhenFilePathIsNotFound() { // Arrange + var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); + var settingsUtils = new SettingsUtils(mockIOProvider.Object); string file_name_random = "test\\" + RandomString(); string file_name_exists = "test\\exists"; string file_contents_correct_json_content = "{\"name\":\"powertoy module name\",\"version\":\"powertoy version\"}"; // Act - bool pathNotFound = SettingsUtils.SettingsFolderExists(file_name_random); + bool pathNotFound = settingsUtils.SettingsExists(file_name_random); - SettingsUtils.SaveSettings(file_contents_correct_json_content, file_name_exists); - bool pathFound = SettingsUtils.SettingsFolderExists(file_name_exists); + settingsUtils.SaveSettings(file_contents_correct_json_content, file_name_exists); + bool pathFound = settingsUtils.SettingsExists(file_name_exists); // Assert Assert.IsFalse(pathNotFound); Assert.IsTrue(pathFound); } - [TestMethod] - public void CreateSettingsFolder_ShouldCreateFolder_WhenSuccessful() - { - // Arrange - string file_name = "test\\" + RandomString(); - - // Act - SettingsUtils.CreateSettingsFolder(file_name); - - // Assert - Assert.IsTrue(SettingsUtils.SettingsFolderExists(file_name)); - } - - public void DeleteFolder(string powertoy) - { - Directory.Delete(Path.Combine(SettingsUtils.LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}"), true); - } - public static string RandomString() { Random random = new Random(); diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs index 6265b658a3..0d31233992 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs @@ -6,58 +6,15 @@ using System.IO; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Lib; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; +using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; namespace ViewModelTests { [TestClass] public class ColorPicker { - private const string ModuleName = "ColorPicker"; - - [TestInitialize] - public void Setup() - { - var generalSettings = new GeneralSettings(); - var colorPickerSettings = new ColorPickerSettings(); - - SettingsUtils.SaveSettings(generalSettings.ToJsonString()); - SettingsUtils.SaveSettings(colorPickerSettings.ToJsonString(), colorPickerSettings.Name, ModuleName + ".json"); - } - - [TestCleanup] - public void CleanUp() - { - string generalSettings_file_name = string.Empty; - if (SettingsUtils.SettingsFolderExists(generalSettings_file_name)) - { - DeleteFolder(generalSettings_file_name); - } - - if (SettingsUtils.SettingsFolderExists(ModuleName)) - { - DeleteFolder(ModuleName); - } - } - - [TestMethod] - public void ColorPickerIsEnabledByDefault() - { - var viewModel = new ColorPickerViewModel(ColorPickerIsEnabledByDefault_IPC); - - Assert.IsTrue(viewModel.IsEnabled); - } - - public int ColorPickerIsEnabledByDefault_IPC(string msg) - { - OutGoingGeneralSettings snd = JsonSerializer.Deserialize(msg); - Assert.IsTrue(snd.GeneralSettings.Enabled.ColorPicker); - return 0; - } - - private static void DeleteFolder(string powertoy) - { - Directory.Delete(Path.Combine(SettingsUtils.LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}"), true); - } + } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/FancyZones.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/FancyZones.cs index aef1679f8a..a4e1a4984a 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/FancyZones.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/FancyZones.cs @@ -9,7 +9,9 @@ using System.Text.Json; using CommonLibTest; using Microsoft.PowerToys.Settings.UI.Lib; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; +using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; namespace ViewModelTests { @@ -18,39 +20,6 @@ namespace ViewModelTests { public const string FancyZonesTestFolderName = "Test\\FancyZones"; - [TestInitialize] - public void Setup() - { - // initialize creation of test settings file. - GeneralSettings generalSettings = new GeneralSettings(); - FZConfigProperties fZConfigProperties = new FZConfigProperties(); - - SettingsUtils.SaveSettings(generalSettings.ToJsonString()); - SettingsUtils.SaveSettings(fZConfigProperties.ToJsonString(), FancyZonesTestFolderName); - } - - [TestCleanup] - public void CleanUp() - { - // delete general settings folder created. - string generalSettings_file_name = string.Empty; - if (SettingsUtils.SettingsFolderExists(string.Empty)) - { - DeleteFolder(string.Empty); - } - - // delete fancy zones folder created. - if (SettingsUtils.SettingsFolderExists(FancyZonesTestFolderName)) - { - DeleteFolder(FancyZonesTestFolderName); - } - } - - public void DeleteFolder(string powertoy) - { - Directory.Delete(Path.Combine(SettingsUtils.LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}"), true); - } - [TestMethod] public void IsEnabled_ShouldDisableModule_WhenSuccessful() { @@ -62,7 +31,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsTrue(viewModel.IsEnabled); // check if the module is enabled. // act @@ -81,7 +50,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsTrue(viewModel.ShiftDrag); // check if value was initialized to false. // act @@ -100,7 +69,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.OverrideSnapHotkeys); // check if value was initialized to false. // act @@ -119,7 +88,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.MoveWindowsBasedOnPosition); // check if value was initialized to false. // act @@ -138,7 +107,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.MakeDraggedWindowsTransparent); // check if value was initialized to false. // act @@ -157,7 +126,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.MouseSwitch); // check if value was initialized to false. // act @@ -176,7 +145,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.DisplayChangeMoveWindows); // check if value was initialized to false. // act @@ -195,7 +164,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.ZoneSetChangeMoveWindows); // check if value was initialized to false. // act @@ -214,7 +183,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.AppLastZoneMoveWindows); // check if value was initialized to false. // act @@ -232,7 +201,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.OpenWindowOnActiveMonitor); // check if value was initialized to false. // act @@ -251,7 +220,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.RestoreSize); // check if value was initialized to false. // act @@ -270,7 +239,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsTrue(viewModel.UseCursorPosEditorStartupScreen); // check if value was initialized to false. // act @@ -289,7 +258,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.ShowOnAllMonitors); // check if value was initialized to false. // act @@ -308,7 +277,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.AreEqual(ConfigDefaults.DefaultFancyZonesZoneHighlightColor, viewModel.ZoneHighlightColor); // act @@ -327,7 +296,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.AreEqual(ConfigDefaults.DefaultFancyzonesBorderColor, viewModel.ZoneBorderColor); // act @@ -346,7 +315,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.AreEqual(ConfigDefaults.DefaultFancyZonesInActiveColor, viewModel.ZoneInActiveColor); // act @@ -365,7 +334,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.AreEqual(string.Empty, viewModel.ExcludedApps); // act @@ -384,7 +353,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.AreEqual(50, viewModel.HighlightOpacity); // act diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/General.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/General.cs index 45e4660f85..f696f24f97 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/General.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/General.cs @@ -8,6 +8,8 @@ using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Lib; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using NuGet.Frameworks; namespace ViewModelTests { @@ -16,29 +18,6 @@ namespace ViewModelTests { public const string generalSettings_file_name = "Test\\GenealSettings"; - [TestInitialize] - public void Setup() - { - // initialize creation of test settings file. - GeneralSettings generalSettings = new GeneralSettings(); - SettingsUtils.SaveSettings(generalSettings.ToJsonString(), generalSettings_file_name); - } - - [TestCleanup] - public void CleanUp() - { - // delete folder created. - if (SettingsUtils.SettingsFolderExists(generalSettings_file_name)) - { - DeleteFolder(generalSettings_file_name); - } - } - - public void DeleteFolder(string powertoy) - { - Directory.Delete(Path.Combine(SettingsUtils.LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}"), true); - } - [TestMethod] public void IsElevated_ShouldUpdateRunasAdminStatusAttrs_WhenSuccessful() { @@ -47,6 +26,7 @@ namespace ViewModelTests Func SendRestartAdminIPCMessage = msg => { return 0; }; Func SendCheckForUpdatesIPCMessage = msg => { return 0; }; GeneralViewModel viewModel = new GeneralViewModel( + new Mock().Object, "GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsUserText", false, @@ -83,6 +63,7 @@ namespace ViewModelTests Func SendRestartAdminIPCMessage = msg => { return 0; }; Func SendCheckForUpdatesIPCMessage = msg => { return 0; }; GeneralViewModel viewModel = new GeneralViewModel( + new Mock().Object, "GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsUserText", false, @@ -114,6 +95,7 @@ namespace ViewModelTests // Arrange GeneralViewModel viewModel = new GeneralViewModel( + new Mock().Object, "GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsUserText", false, @@ -146,6 +128,7 @@ namespace ViewModelTests Func SendRestartAdminIPCMessage = msg => { return 0; }; Func SendCheckForUpdatesIPCMessage = msg => { return 0; }; viewModel = new GeneralViewModel( + new Mock().Object, "GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsUserText", false, @@ -176,6 +159,7 @@ namespace ViewModelTests Func SendRestartAdminIPCMessage = msg => { return 0; }; Func SendCheckForUpdatesIPCMessage = msg => { return 0; }; GeneralViewModel viewModel = new GeneralViewModel( + new Mock().Object, "GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsUserText", false, @@ -193,6 +177,24 @@ namespace ViewModelTests viewModel.IsDarkThemeRadioButtonChecked = true; } + [TestMethod] + public void AllModulesAreEnabledByDefault() + { + //arrange + EnabledModules modules = new EnabledModules(); + + + //Assert + Assert.IsTrue(modules.FancyZones); + Assert.IsTrue(modules.ImageResizer); + Assert.IsTrue(modules.FileExplorerPreview); + Assert.IsTrue(modules.ShortcutGuide); + Assert.IsTrue(modules.PowerRename); + Assert.IsTrue(modules.KeyboardManager); + Assert.IsTrue(modules.PowerLauncher); + Assert.IsTrue(modules.ColorPicker); + } + public int UpdateUIThemeMethod(string themeName) { return 0; diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ImageResizer.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ImageResizer.cs index 1e18d186ca..0e4ef3e16f 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ImageResizer.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ImageResizer.cs @@ -7,8 +7,11 @@ using System.IO; using System.Linq; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; +using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; namespace ViewModelTests { @@ -17,43 +20,11 @@ namespace ViewModelTests { public const string Module = "ImageResizer"; - [TestInitialize] - public void Setup() - { - // initialize creation of test settings file. - // Test base path: - // C:\Users\\AppData\Local\Packages\08e1807b-8b6d-4bfa-adc4-79c64aae8e78_9abkseg265h2m\LocalState\Microsoft\PowerToys\ - GeneralSettings generalSettings = new GeneralSettings(); - ImageResizerSettings imageResizer = new ImageResizerSettings(); - - SettingsUtils.SaveSettings(generalSettings.ToJsonString()); - SettingsUtils.SaveSettings(imageResizer.ToJsonString(), imageResizer.Name); - } - - [TestCleanup] - public void CleanUp() - { - // delete folder created. - string generalSettings_file_name = string.Empty; - if (SettingsUtils.SettingsFolderExists(generalSettings_file_name)) - { - DeleteFolder(generalSettings_file_name); - } - - if (SettingsUtils.SettingsFolderExists(Module)) - { - DeleteFolder(Module); - } - } - - public void DeleteFolder(string powertoy) - { - Directory.Delete(Path.Combine(SettingsUtils.LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}"), true); - } - [TestMethod] public void IsEnabled_ShouldEnableModule_WhenSuccessful() { + var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + // Assert Func SendMockIPCConfigMSG = msg => { @@ -63,7 +34,7 @@ namespace ViewModelTests }; // arrange - ImageResizerViewModel viewModel = new ImageResizerViewModel(SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils.Object, SendMockIPCConfigMSG); // act viewModel.IsEnabled = true; @@ -73,14 +44,16 @@ namespace ViewModelTests public void JPEGQualityLevel_ShouldSetValueToTen_WhenSuccessful() { // arrange + var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); + var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); // act viewModel.JPEGQualityLevel = 10; // Assert - viewModel = new ImageResizerViewModel(SendMockIPCConfigMSG); + viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); Assert.AreEqual(10, viewModel.JPEGQualityLevel); } @@ -88,14 +61,16 @@ namespace ViewModelTests public void PngInterlaceOption_ShouldSetValueToTen_WhenSuccessful() { // arrange + var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); + var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); // act viewModel.PngInterlaceOption = 10; // Assert - viewModel = new ImageResizerViewModel(SendMockIPCConfigMSG); + viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); Assert.AreEqual(10, viewModel.PngInterlaceOption); } @@ -103,14 +78,16 @@ namespace ViewModelTests public void TiffCompressOption_ShouldSetValueToTen_WhenSuccessful() { // arrange + var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); + var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); // act viewModel.TiffCompressOption = 10; // Assert - viewModel = new ImageResizerViewModel(SendMockIPCConfigMSG); + viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); Assert.AreEqual(10, viewModel.TiffCompressOption); } @@ -118,15 +95,17 @@ namespace ViewModelTests public void FileName_ShouldUpdateValue_WhenSuccessful() { // arrange + var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); + var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); string expectedValue = "%1 (%3)"; // act viewModel.FileName = expectedValue; // Assert - viewModel = new ImageResizerViewModel(SendMockIPCConfigMSG); + viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); Assert.AreEqual(expectedValue, viewModel.FileName); } @@ -134,29 +113,39 @@ namespace ViewModelTests public void KeepDateModified_ShouldUpdateValue_WhenSuccessful() { // arrange + var settingUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + + var expectedSettingsString = new ImageResizerSettings() { Properties = new ImageResizerProperties() { ImageresizerKeepDateModified = new BoolProperty() { Value = true } } }.ToJsonString(); + settingUtils.Setup(x => x.SaveSettings( + It.Is(content => content.Equals(expectedSettingsString, StringComparison.Ordinal)), + It.Is(module => module.Equals(Module, StringComparison.Ordinal)), + It.IsAny())) + .Verifiable(); + Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(settingUtils.Object, SendMockIPCConfigMSG); // act viewModel.KeepDateModified = true; // Assert - ImageResizerSettings settings = SettingsUtils.GetSettings(Module); - Assert.AreEqual(true, settings.Properties.ImageresizerKeepDateModified.Value); + settingUtils.Verify(); } [TestMethod] public void Encoder_ShouldUpdateValue_WhenSuccessful() { // arrange + var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); + var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); // act viewModel.Encoder = 3; // Assert - viewModel = new ImageResizerViewModel(SendMockIPCConfigMSG); + viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); Assert.AreEqual("163bcc30-e2e9-4f0b-961d-a3e9fdb788a3", viewModel.GetEncoderGuid(viewModel.Encoder)); Assert.AreEqual(3, viewModel.Encoder); } @@ -165,8 +154,9 @@ namespace ViewModelTests public void AddRow_ShouldAddEmptyImageSize_WhenSuccessful() { // arrange + var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils.Object, SendMockIPCConfigMSG); int sizeOfOriginalArray = viewModel.Sizes.Count; // act @@ -180,8 +170,9 @@ namespace ViewModelTests public void DeleteImageSize_ShouldDeleteImageSize_WhenSuccessful() { // arrange + var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils.Object, SendMockIPCConfigMSG); int sizeOfOriginalArray = viewModel.Sizes.Count; ImageSize deleteCandidate = viewModel.Sizes.Where(x => x.Id == 0).First(); diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs index d2c7db9365..c1adf71bd0 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs @@ -5,6 +5,7 @@ using Microsoft.PowerToys.Settings.UI.Lib; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; namespace ViewModelTests { @@ -24,13 +25,11 @@ namespace ViewModelTests private PowerLauncherViewModel viewModel; private PowerLauncherSettings mockSettings; private SendCallbackMock sendCallbackMock; - [TestInitialize] public void Initialize() { mockSettings = new PowerLauncherSettings(); sendCallbackMock = new SendCallbackMock(); - viewModel = new PowerLauncherViewModel( mockSettings, new PowerLauncherViewModel.SendCallback(sendCallbackMock.OnSend)); diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs index d319e1cbb9..7833d2ff7e 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs @@ -7,7 +7,9 @@ using System.IO; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Lib; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; +using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; namespace ViewModelTests { @@ -16,38 +18,6 @@ namespace ViewModelTests { public const string Module = "Test\\File Explorer"; - [TestInitialize] - public void Setup() - { - // initialize creation of test settings file. - GeneralSettings generalSettings = new GeneralSettings(); - PowerPreviewSettings powerpreview = new PowerPreviewSettings(); - - SettingsUtils.SaveSettings(generalSettings.ToJsonString()); - SettingsUtils.SaveSettings(powerpreview.ToJsonString(), powerpreview.Name); - } - - [TestCleanup] - public void CleanUp() - { - // delete folder created. - string generalSettings_file_name = string.Empty; - if (SettingsUtils.SettingsFolderExists(generalSettings_file_name)) - { - DeleteFolder(generalSettings_file_name); - } - - if (SettingsUtils.SettingsFolderExists(Module)) - { - DeleteFolder(Module); - } - } - - public void DeleteFolder(string powertoy) - { - Directory.Delete(Path.Combine(SettingsUtils.LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}"), true); - } - [TestMethod] public void SVGRenderIsEnabled_ShouldPrevHandler_WhenSuccessful() { @@ -60,7 +30,7 @@ namespace ViewModelTests }; // arrange - PowerPreviewViewModel viewModel = new PowerPreviewViewModel(SendMockIPCConfigMSG, Module); + PowerPreviewViewModel viewModel = new PowerPreviewViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, Module); // act viewModel.SVGRenderIsEnabled = true; @@ -78,7 +48,7 @@ namespace ViewModelTests }; // arrange - PowerPreviewViewModel viewModel = new PowerPreviewViewModel(SendMockIPCConfigMSG, Module); + PowerPreviewViewModel viewModel = new PowerPreviewViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, Module); // act viewModel.SVGThumbnailIsEnabled = true; @@ -96,7 +66,7 @@ namespace ViewModelTests }; // arrange - PowerPreviewViewModel viewModel = new PowerPreviewViewModel(SendMockIPCConfigMSG, Module);; + PowerPreviewViewModel viewModel = new PowerPreviewViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, Module);; // act viewModel.MDRenderIsEnabled = true; diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerRename.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerRename.cs index 10cd6f918b..9f9a9d809e 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerRename.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerRename.cs @@ -7,7 +7,9 @@ using System.IO; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Lib; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; +using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; namespace ViewModelTests { @@ -16,26 +18,6 @@ namespace ViewModelTests { public const string ModuleName = "PowerRename"; public const string generalSettings_file_name = "Test\\PowerRename"; - [TestInitialize] - public void Setup() - { - // initialize creation of test settings file. - GeneralSettings generalSettings = new GeneralSettings(); - PowerRenameSettings powerRename = new PowerRenameSettings(); - - SettingsUtils.SaveSettings(generalSettings.ToJsonString()); - SettingsUtils.SaveSettings(powerRename.ToJsonString(), generalSettings_file_name, "power-rename-settings.json"); - } - - [TestCleanup] - public void CleanUp() - { - // delete folder created. - if (SettingsUtils.SettingsFolderExists(generalSettings_file_name)) - { - DeleteFolder(generalSettings_file_name); - } - } [TestMethod] public void IsEnabled_ShouldEnableModule_WhenSuccessful() @@ -49,7 +31,7 @@ namespace ViewModelTests }; // arrange - PowerRenameViewModel viewModel = new PowerRenameViewModel(SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); // act viewModel.IsEnabled = true; @@ -67,7 +49,7 @@ namespace ViewModelTests }; // arrange - PowerRenameViewModel viewModel = new PowerRenameViewModel(SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); // act viewModel.MRUEnabled = true; @@ -77,7 +59,7 @@ namespace ViewModelTests public void WhenIsEnabledIsOffAndMRUEnabledIsOffGlobalAndMruShouldBeOff() { Func SendMockIPCConfigMSG = msg => { return 0; }; - PowerRenameViewModel viewModel = new PowerRenameViewModel(SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); viewModel.IsEnabled = false; viewModel.MRUEnabled = false; @@ -89,7 +71,7 @@ namespace ViewModelTests public void WhenIsEnabledIsOffAndMRUEnabledIsOnGlobalAndMruShouldBeOff() { Func SendMockIPCConfigMSG = msg => { return 0; }; - PowerRenameViewModel viewModel = new PowerRenameViewModel(SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); viewModel.IsEnabled = false; viewModel.MRUEnabled = true; @@ -101,7 +83,7 @@ namespace ViewModelTests public void WhenIsEnabledIsOnAndMRUEnabledIsOffGlobalAndMruShouldBeOff() { Func SendMockIPCConfigMSG = msg => { return 0; }; - PowerRenameViewModel viewModel = new PowerRenameViewModel(SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); viewModel.IsEnabled = true; viewModel.MRUEnabled = false; @@ -113,7 +95,7 @@ namespace ViewModelTests public void WhenIsEnabledIsOnAndMRUEnabledIsOnGlobalAndMruShouldBeOn() { Func SendMockIPCConfigMSG = msg => { return 0; }; - PowerRenameViewModel viewModel = new PowerRenameViewModel(SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); viewModel.IsEnabled = true; viewModel.MRUEnabled = true; @@ -133,7 +115,7 @@ namespace ViewModelTests }; // arrange - PowerRenameViewModel viewModel = new PowerRenameViewModel(SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); // act viewModel.EnabledOnContextMenu = true; @@ -151,7 +133,7 @@ namespace ViewModelTests }; // arrange - PowerRenameViewModel viewModel = new PowerRenameViewModel(SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); // act viewModel.EnabledOnContextMenu = true; @@ -169,7 +151,7 @@ namespace ViewModelTests }; // arrange - PowerRenameViewModel viewModel = new PowerRenameViewModel(SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); // act viewModel.RestoreFlagsOnLaunch = true; @@ -187,15 +169,10 @@ namespace ViewModelTests }; // arrange - PowerRenameViewModel viewModel = new PowerRenameViewModel(SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); // act viewModel.MaxDispListNum = 20; } - - public void DeleteFolder(string powertoy) - { - Directory.Delete(Path.Combine(SettingsUtils.LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}"), true); - } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs index 7401f6d319..d788154ffd 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs @@ -7,7 +7,9 @@ using System.IO; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Lib; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; +using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; namespace ViewModelTests { @@ -16,42 +18,6 @@ namespace ViewModelTests { public const string ShortCutGuideTestFolderName = "Test\\ShortCutGuide"; - [TestInitialize] - public void Setup() - { - // initialize creation of test settings file. - // Test base path: - // C:\Users\\AppData\Local\Packages\08e1807b-8b6d-4bfa-adc4-79c64aae8e78_9abkseg265h2m\LocalState\Microsoft\PowerToys\ - GeneralSettings generalSettings = new GeneralSettings(); - ShortcutGuideSettings shortcutGuide = new ShortcutGuideSettings(); - - SettingsUtils.SaveSettings(generalSettings.ToJsonString()); - SettingsUtils.SaveSettings(shortcutGuide.ToJsonString(), ShortCutGuideTestFolderName); - } - - [TestCleanup] - public void CleanUp() - { - // delete folder created. - // delete general settings folder. - string ShortCutGuideTestFolderName = string.Empty; - if (SettingsUtils.SettingsFolderExists(string.Empty)) - { - DeleteFolder(string.Empty); - } - - // delete power rename folder. - if (SettingsUtils.SettingsFolderExists(ShortCutGuideTestFolderName)) - { - DeleteFolder(ShortCutGuideTestFolderName); - } - } - - public void DeleteFolder(string powertoy) - { - Directory.Delete(Path.Combine(SettingsUtils.LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}"), true); - } - [TestMethod] public void IsEnabled_ShouldEnableModule_WhenSuccessful() { @@ -65,7 +31,7 @@ namespace ViewModelTests }; // Arrange - ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(SendMockIPCConfigMSG, ShortCutGuideTestFolderName); + ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, ShortCutGuideTestFolderName); // Act viewModel.IsEnabled = true; @@ -84,7 +50,7 @@ namespace ViewModelTests }; // Arrange - ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(SendMockIPCConfigMSG, ShortCutGuideTestFolderName); + ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, ShortCutGuideTestFolderName); Assert.AreEqual(1, viewModel.ThemeIndex); // Act @@ -104,7 +70,7 @@ namespace ViewModelTests }; // Arrange - ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(SendMockIPCConfigMSG, ShortCutGuideTestFolderName); + ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, ShortCutGuideTestFolderName); Assert.AreEqual(900, viewModel.PressTime); // Act @@ -126,7 +92,7 @@ namespace ViewModelTests }; // Arrange - ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(SendMockIPCConfigMSG, ShortCutGuideTestFolderName); + ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, ShortCutGuideTestFolderName); Assert.AreEqual(90, viewModel.OverlayOpacity); // Act diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Converters/ModuleEnabledToForegroundConverter.cs b/src/core/Microsoft.PowerToys.Settings.UI/Converters/ModuleEnabledToForegroundConverter.cs index bc0558c153..72cf1129c4 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Converters/ModuleEnabledToForegroundConverter.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Converters/ModuleEnabledToForegroundConverter.cs @@ -4,6 +4,7 @@ using System; using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; using Windows.UI.Xaml; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Media; @@ -12,10 +13,12 @@ namespace Microsoft.PowerToys.Settings.UI.Converters { public sealed class ModuleEnabledToForegroundConverter : IValueConverter { + private readonly ISettingsUtils settingsUtils = new SettingsUtils(new SystemIOProvider()); + public object Convert(object value, Type targetType, object parameter, string language) { bool isEnabled = (bool)value; - GeneralSettings generalSettings = SettingsUtils.GetSettings(string.Empty); + GeneralSettings generalSettings = settingsUtils.GetSettings(string.Empty); var defaultTheme = new Windows.UI.ViewManagement.UISettings(); var uiTheme = defaultTheme.GetColorValue(Windows.UI.ViewManagement.UIColorType.Background).ToString(); diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs index 4d759ebde0..6e47d7c8cf 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs @@ -2,6 +2,8 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; using Windows.UI.Xaml.Controls; @@ -13,7 +15,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views public ColorPickerPage() { - ViewModel = new ColorPickerViewModel(ShellPage.SendDefaultIPCMessage); + var settingsUtils = new SettingsUtils(new SystemIOProvider()); + ViewModel = new ColorPickerViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; InitializeComponent(); } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml.cs index 0d02fa241d..56b513e517 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml.cs @@ -2,6 +2,8 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; using Windows.UI.Xaml.Controls; @@ -14,7 +16,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views public FancyZonesPage() { InitializeComponent(); - ViewModel = new FancyZonesViewModel(ShellPage.SendDefaultIPCMessage); + var settingsUtils = new SettingsUtils(new SystemIOProvider()); + ViewModel = new FancyZonesViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml.cs index 8e7cd9c732..459679c753 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; using Windows.ApplicationModel.Resources; using Windows.Data.Json; @@ -31,8 +33,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views // Load string resources ResourceLoader loader = ResourceLoader.GetForViewIndependentUse(); + var settingsUtils = new SettingsUtils(new SystemIOProvider()); ViewModel = new GeneralViewModel( + settingsUtils, loader.GetString("GeneralSettings_RunningAsAdminText"), loader.GetString("GeneralSettings_RunningAsUserText"), ShellPage.IsElevated, diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml.cs index b666c48709..7f8281679d 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml.cs @@ -2,6 +2,8 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -16,7 +18,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views { InitializeComponent(); - ViewModel = new ImageResizerViewModel(ShellPage.SendDefaultIPCMessage); + var settingsUtils = new SettingsUtils(new SystemIOProvider()); + ViewModel = new ImageResizerViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/KeyboardManagerPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/KeyboardManagerPage.xaml.cs index a6a14d6198..2333badd6b 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/KeyboardManagerPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/KeyboardManagerPage.xaml.cs @@ -31,7 +31,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views { dispatcher = Window.Current.Dispatcher; - ViewModel = new KeyboardManagerViewModel(ShellPage.SendDefaultIPCMessage, FilterRemapKeysList); + var settingsUtils = new SettingsUtils(new SystemIOProvider()); + ViewModel = new KeyboardManagerViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage, FilterRemapKeysList); watcher = Helper.GetFileWatcher( PowerToyName, diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml.cs index f5b2e115db..b41fa6f596 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml.cs @@ -4,6 +4,8 @@ using System; using System.Collections.ObjectModel; +using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; using Windows.UI.Xaml.Controls; @@ -19,7 +21,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views public PowerLauncherPage() { InitializeComponent(); - ViewModel = new PowerLauncherViewModel(ShellPage.SendDefaultIPCMessage, (int)Windows.System.VirtualKey.Space); + var settingsUtils = new SettingsUtils(new SystemIOProvider()); + ViewModel = new PowerLauncherViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage, (int)Windows.System.VirtualKey.Space); DataContext = ViewModel; var loader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView(); diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml.cs index 527370189c..fe71b790a4 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml.cs @@ -2,6 +2,8 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; using Windows.UI.Xaml.Controls; @@ -17,7 +19,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views public PowerPreviewPage() { InitializeComponent(); - ViewModel = new PowerPreviewViewModel(ShellPage.SendDefaultIPCMessage); + var settingsUtils = new SettingsUtils(new SystemIOProvider()); + ViewModel = new PowerPreviewViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerRenamePage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerRenamePage.xaml.cs index c268302f1a..7f44b3672a 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerRenamePage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerRenamePage.xaml.cs @@ -2,6 +2,8 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; using Windows.UI.Xaml.Controls; @@ -14,7 +16,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views public PowerRenamePage() { InitializeComponent(); - ViewModel = new PowerRenameViewModel(ShellPage.SendDefaultIPCMessage); + var settingsUtils = new SettingsUtils(new SystemIOProvider()); + ViewModel = new PowerRenameViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/ShortcutGuidePage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/ShortcutGuidePage.xaml.cs index 0bc20f05fc..c968dc8cd9 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ShortcutGuidePage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ShortcutGuidePage.xaml.cs @@ -2,6 +2,8 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; using Windows.UI.Xaml.Controls; @@ -14,8 +16,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views public ShortcutGuidePage() { InitializeComponent(); - - ViewModel = new ShortcutGuideViewModel(ShellPage.SendDefaultIPCMessage); + var settingsUtils = new SettingsUtils(new SystemIOProvider()); + ViewModel = new ShortcutGuideViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } } diff --git a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs index 18e928f13a..87466f352a 100644 --- a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs +++ b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs @@ -15,6 +15,7 @@ namespace ColorPicker.Settings [Export(typeof(IUserSettings))] public class UserSettings : IUserSettings { + private readonly ISettingsUtils _settingsUtils; private const string ColorPickerModuleName = "ColorPicker"; private const string DefaultActivationShortcut = "Ctrl + Break"; private const int MaxNumberOfRetry = 5; @@ -27,6 +28,7 @@ namespace ColorPicker.Settings [ImportingConstructor] public UserSettings() { + _settingsUtils = new SettingsUtils(new SystemIOProvider()); ChangeCursor = new SettingItem(true); ActivationShortcut = new SettingItem(DefaultActivationShortcut); CopiedColorRepresentation = new SettingItem(ColorRepresentationType.HEX); @@ -56,14 +58,14 @@ namespace ColorPicker.Settings { retryCount++; - if (!SettingsUtils.SettingsExists(ColorPickerModuleName)) + if (!_settingsUtils.SettingsExists(ColorPickerModuleName)) { Logger.LogInfo("ColorPicker settings.json was missing, creating a new one"); var defaultColorPickerSettings = new ColorPickerSettings(); - defaultColorPickerSettings.Save(); + defaultColorPickerSettings.Save(_settingsUtils); } - var settings = SettingsUtils.GetSettings(ColorPickerModuleName); + var settings = _settingsUtils.GetSettings(ColorPickerModuleName); if (settings != null) { ChangeCursor.Value = settings.Properties.ChangeCursor; diff --git a/src/modules/launcher/PowerLauncher/SettingsWatcher.cs b/src/modules/launcher/PowerLauncher/SettingsWatcher.cs index 46953868b5..1c1c318c64 100644 --- a/src/modules/launcher/PowerLauncher/SettingsWatcher.cs +++ b/src/modules/launcher/PowerLauncher/SettingsWatcher.cs @@ -7,6 +7,7 @@ using System.IO; using System.Threading; using System.Windows.Input; using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; using PowerLauncher.Helper; using Wox.Core.Plugin; using Wox.Infrastructure.Hotkey; @@ -20,6 +21,8 @@ namespace PowerLauncher // Watch for /Local/Microsoft/PowerToys/Launcher/Settings.json changes public class SettingsWatcher : BaseModel { + private readonly ISettingsUtils _settingsUtils; + private const int MaxRetries = 10; private static readonly object _watcherSyncObject = new object(); private readonly FileSystemWatcher _watcher; @@ -27,6 +30,7 @@ namespace PowerLauncher public SettingsWatcher(Settings settings) { + _settingsUtils = new SettingsUtils(new SystemIOProvider()); _settings = settings; // Set up watcher @@ -36,13 +40,14 @@ namespace PowerLauncher OverloadSettings(); } - public static void CreateSettingsIfNotExists() + public void CreateSettingsIfNotExists() { - if (!SettingsUtils.SettingsExists(PowerLauncherSettings.ModuleName)) + if (!_settingsUtils.SettingsExists(PowerLauncherSettings.ModuleName)) { Log.Info("|SettingsWatcher.OverloadSettings|PT Run settings.json was missing, creating a new one"); + var defaultSettings = new PowerLauncherSettings(); - defaultSettings.Save(); + defaultSettings.Save(_settingsUtils); } } @@ -58,7 +63,7 @@ namespace PowerLauncher retryCount++; CreateSettingsIfNotExists(); - var overloadSettings = SettingsUtils.GetSettings(PowerLauncherSettings.ModuleName); + var overloadSettings = _settingsUtils.GetSettings(PowerLauncherSettings.ModuleName); var openPowerlauncher = ConvertHotkey(overloadSettings.Properties.OpenPowerLauncher); if (_settings.Hotkey != openPowerlauncher) @@ -121,7 +126,7 @@ namespace PowerLauncher // Settings.json could possibly be corrupted. To mitigate this we delete the // current file and replace it with a correct json value. - SettingsUtils.DeleteSettings(PowerLauncherSettings.ModuleName); + _settingsUtils.DeleteSettings(PowerLauncherSettings.ModuleName); CreateSettingsIfNotExists(); ErrorReporting.ShowMessageBox(Properties.Resources.deseralization_error_title, Properties.Resources.deseralization_error_message); } From 57972c6653a22d8736ce963d22e4530857d56302 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Mon, 21 Sep 2020 13:43:23 -0700 Subject: [PATCH 04/22] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 926efe9137..64c79e9f4f 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ Preview Pane is an existing feature in the File Explorer. To enable it, you jus ### Video Conference Mute (Experimental) -[](https://aka.ms/PowerToysOverview_VideoConference) [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) is a quick and easy way to do an global "mute" of both your microphone and webcam via Win+N. Just set your webcam in the target application to the PowerToys VideoConference camera. +[](https://aka.ms/PowerToysOverview_VideoConference) [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) is a quick and easy way to do a global "mute" of both your microphone and webcam via Win+N. Just set your webcam in the target application to the PowerToys VideoConference camera. **Note:** This is only included in the [pre-release version of PowerToys installer][github-prerelease-link]. This PowerToy requires Windows 10 1903 (build 18362) or later.
From 934949725b3e17e43d34dddc936e3cc315e0cea6 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Mon, 21 Sep 2020 19:44:12 -0700 Subject: [PATCH 05/22] FxCop and StyleCop for SVG thumbnail (#6757) * FxCop work for SvgThumbnail * enabling stylecop --- installer/PowerToysSetup/Product.wxs | 6 +- .../SvgThumbnailProvider.cs | 117 +++++++++--------- .../SvgThumbnailProvider.csproj | 74 ++++++++++- .../SvgThumbnailProvider/packages.config | 9 ++ .../SvgThumbnailProviderTests.cs | 19 +-- 5 files changed, 156 insertions(+), 69 deletions(-) create mode 100644 src/modules/previewpane/SvgThumbnailProvider/packages.config diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 59c1b28f89..8d57f38edd 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -438,18 +438,18 @@ - + - + - + diff --git a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs index 8930516a37..925091ae7b 100644 --- a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs +++ b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs @@ -1,24 +1,22 @@ // 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.Drawing; +using System.Drawing.Drawing2D; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Windows.Forms; +using Common.ComInterlop; +using Common.Utilities; +using Microsoft.PowerToys.Telemetry; +using PreviewHandlerCommon; -namespace SvgThumbnailProvider +namespace Microsoft.PowerToys.ThumbnailHandler.Svg { - using System; - using System.Drawing; - using System.Drawing.Drawing2D; - using System.Drawing.Imaging; - using System.IO; - using System.Linq; - using System.Runtime.InteropServices; - using System.Runtime.InteropServices.ComTypes; - using System.Windows.Forms; - using Common; - using Common.ComInterlop; - using Common.Utilities; - using Microsoft.PowerToys.Telemetry; - using PreviewHandlerCommon; - /// /// SVG Thumbnail Provider. /// @@ -64,7 +62,7 @@ namespace SvgThumbnailProvider { deviceContextHandle = graphics.GetHdc(); - IViewObject viewObject = browser.ActiveXInstance as IViewObject; + IViewObject viewObject = browser?.ActiveXInstance as IViewObject; viewObject.Draw(1, -1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, deviceContextHandle, ref rect, IntPtr.Zero, IntPtr.Zero, 0); } finally @@ -100,30 +98,16 @@ namespace SvgThumbnailProvider // Wrap the SVG content in HTML in IE Edge mode so we can ensure // we render properly. string wrappedContent = WrapSVGInHTML(content); - WebBrowserExt browser = new WebBrowserExt(); - browser.Dock = DockStyle.Fill; - browser.IsWebBrowserContextMenuEnabled = false; - browser.ScriptErrorsSuppressed = true; - browser.ScrollBarsEnabled = false; - browser.AllowNavigation = false; - browser.Width = (int)cx; - browser.Height = (int)cx; - browser.DocumentText = wrappedContent; - - // Wait for the browser to render the content. - while (browser.IsBusy || browser.ReadyState != WebBrowserReadyState.Complete) + using (WebBrowserExt browser = new WebBrowserExt()) { - Application.DoEvents(); - } - - // Check size of the rendered SVG. - var svg = browser.Document.GetElementsByTagName("svg").Cast().FirstOrDefault(); - if (svg != null) - { - // Update the size of the browser control to fit the SVG - // in the visible viewport. - browser.Width = svg.OffsetRectangle.Width; - browser.Height = svg.OffsetRectangle.Height; + browser.Dock = DockStyle.Fill; + browser.IsWebBrowserContextMenuEnabled = false; + browser.ScriptErrorsSuppressed = true; + browser.ScrollBarsEnabled = false; + browser.AllowNavigation = false; + browser.Width = (int)cx; + browser.Height = (int)cx; + browser.DocumentText = wrappedContent; // Wait for the browser to render the content. while (browser.IsBusy || browser.ReadyState != WebBrowserReadyState.Complete) @@ -131,16 +115,32 @@ namespace SvgThumbnailProvider Application.DoEvents(); } - // Capture the image of the SVG from the browser. - thumbnail = GetBrowserContentImage(browser, svg.OffsetRectangle, Color.White); - if (thumbnail.Width != cx && thumbnail.Height != cx) + // Check size of the rendered SVG. + var svg = browser.Document.GetElementsByTagName("svg").Cast().FirstOrDefault(); + if (svg != null) { - // We are not the appropriate size for caller. Resize now while - // respecting the aspect ratio. - float scale = Math.Min((float)cx / thumbnail.Width, (float)cx / thumbnail.Height); - int scaleWidth = (int)(thumbnail.Width * scale); - int scaleHeight = (int)(thumbnail.Height * scale); - thumbnail = ResizeImage(thumbnail, scaleWidth, scaleHeight); + // Update the size of the browser control to fit the SVG + // in the visible viewport. + browser.Width = svg.OffsetRectangle.Width; + browser.Height = svg.OffsetRectangle.Height; + + // Wait for the browser to render the content. + while (browser.IsBusy || browser.ReadyState != WebBrowserReadyState.Complete) + { + Application.DoEvents(); + } + + // Capture the image of the SVG from the browser. + thumbnail = GetBrowserContentImage(browser, svg.OffsetRectangle, Color.White); + if (thumbnail.Width != cx && thumbnail.Height != cx) + { + // We are not the appropriate size for caller. Resize now while + // respecting the aspect ratio. + float scale = Math.Min((float)cx / thumbnail.Width, (float)cx / thumbnail.Height); + int scaleWidth = (int)(thumbnail.Width * scale); + int scaleHeight = (int)(thumbnail.Height * scale); + thumbnail = ResizeImage(thumbnail, scaleWidth, scaleHeight); + } } } @@ -166,7 +166,7 @@ namespace SvgThumbnailProvider {0} "; - return string.Format(html, svg); + return string.Format(CultureInfo.CurrentCulture, html, svg); } /// @@ -178,8 +178,11 @@ namespace SvgThumbnailProvider /// The resized image. public static Bitmap ResizeImage(Image image, int width, int height) { - if (width <= 0 || height <= 0 || - width > MaxThumbnailSize || height > MaxThumbnailSize) + if (width <= 0 || + height <= 0 || + width > MaxThumbnailSize || + height > MaxThumbnailSize || + image == null) { return null; } @@ -232,11 +235,13 @@ namespace SvgThumbnailProvider if (svgData != null) { - Bitmap thumbnail = GetThumbnail(svgData, cx); - if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0) + using (Bitmap thumbnail = GetThumbnail(svgData, cx)) { - phbmp = thumbnail.GetHbitmap(); - pdwAlpha = WTS_ALPHATYPE.WTSAT_RGB; + if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0) + { + phbmp = thumbnail.GetHbitmap(); + pdwAlpha = WTS_ALPHATYPE.WTSAT_RGB; + } } } } diff --git a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj index 56a26dce10..8b1170d7be 100644 --- a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj +++ b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj @@ -1,17 +1,58 @@  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Debug AnyCPU {8FFE09DA-FA4F-4EE1-B3A2-AD5497FBD1AD} Library - SVGThumbnailProvider + Microsoft.PowerToys.ThumbnailHandler.Svg SVGThumbnailProvider + SVGThumbnailProvider + PowerToys SvgPreviewHandler + Microsoft Corporation + Copyright (C) 2020 Microsoft Corporation + PowerToys v4.7.2 512 true true + + x64 @@ -40,10 +81,12 @@ + true x64 ..\..\..\..\x64\Debug\modules\FileExplorerPreview\ + true x64 ..\..\..\..\x64\Release\modules\FileExplorerPreview\ @@ -65,6 +108,9 @@ GlobalSuppressions.cs + + Code + @@ -84,5 +130,31 @@ StyleCop.json + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/SvgThumbnailProvider/packages.config b/src/modules/previewpane/SvgThumbnailProvider/packages.config new file mode 100644 index 0000000000..727f3814ea --- /dev/null +++ b/src/modules/previewpane/SvgThumbnailProvider/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/SvgThumbnailProviderTests.cs b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/SvgThumbnailProviderTests.cs index 92b2db8d7c..06ca112f27 100644 --- a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/SvgThumbnailProviderTests.cs +++ b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/SvgThumbnailProviderTests.cs @@ -9,6 +9,7 @@ using System.Runtime.InteropServices.ComTypes; using System.Text; using Common.ComInterlop; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.PowerToys.ThumbnailHandler.Svg; using Moq; namespace SvgThumbnailProviderUnitTests @@ -25,7 +26,7 @@ namespace SvgThumbnailProviderUnitTests svgBuilder.AppendLine("\t"); svgBuilder.AppendLine(""); - Bitmap thumbnail = SvgThumbnailProvider.SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256); + Bitmap thumbnail = SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256); Assert.IsTrue(thumbnail != null); } @@ -39,7 +40,7 @@ namespace SvgThumbnailProviderUnitTests svgBuilder.AppendLine("\t"); svgBuilder.AppendLine(""); - Bitmap thumbnail = SvgThumbnailProvider.SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256); + Bitmap thumbnail = SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256); Assert.IsTrue(thumbnail != null); } @@ -49,21 +50,21 @@ namespace SvgThumbnailProviderUnitTests var svgBuilder = new StringBuilder(); svgBuilder.AppendLine("

foo

"); - Bitmap thumbnail = SvgThumbnailProvider.SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256); + Bitmap thumbnail = SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256); Assert.IsTrue(thumbnail == null); } [TestMethod] public void CheckNoSvgEmptyStringShouldReturnNullBitmap() { - Bitmap thumbnail = SvgThumbnailProvider.SvgThumbnailProvider.GetThumbnail(string.Empty, 256); + Bitmap thumbnail = SvgThumbnailProvider.GetThumbnail(string.Empty, 256); Assert.IsTrue(thumbnail == null); } [TestMethod] public void CheckNoSvgNullStringShouldReturnNullBitmap() { - Bitmap thumbnail = SvgThumbnailProvider.SvgThumbnailProvider.GetThumbnail(null, 256); + Bitmap thumbnail = SvgThumbnailProvider.GetThumbnail(null, 256); Assert.IsTrue(thumbnail == null); } @@ -71,7 +72,7 @@ namespace SvgThumbnailProviderUnitTests public void CheckZeroSizedThumbnailShouldReturnNullBitmap() { string content = ""; - Bitmap thumbnail = SvgThumbnailProvider.SvgThumbnailProvider.GetThumbnail(content, 0); + Bitmap thumbnail = SvgThumbnailProvider.GetThumbnail(content, 0); Assert.IsTrue(thumbnail == null); } @@ -92,7 +93,7 @@ namespace SvgThumbnailProviderUnitTests svgBuilder.AppendLine(""); svgBuilder.AppendLine(""); - Bitmap thumbnail = SvgThumbnailProvider.SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256); + Bitmap thumbnail = SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256); Assert.IsTrue(thumbnail != null); } @@ -105,7 +106,7 @@ namespace SvgThumbnailProviderUnitTests svgBuilder.AppendLine(""); svgBuilder.AppendLine(""); - SvgThumbnailProvider.SvgThumbnailProvider provider = new SvgThumbnailProvider.SvgThumbnailProvider(); + SvgThumbnailProvider provider = new SvgThumbnailProvider(); provider.Initialize(GetMockStream(svgBuilder.ToString()), 0); @@ -134,7 +135,7 @@ namespace SvgThumbnailProviderUnitTests svgBuilder.AppendLine(""); svgBuilder.AppendLine(""); - SvgThumbnailProvider.SvgThumbnailProvider provider = new SvgThumbnailProvider.SvgThumbnailProvider(); + SvgThumbnailProvider provider = new SvgThumbnailProvider(); provider.Initialize(GetMockStream(svgBuilder.ToString()), 0); From d2201cbb791db4f1e1f14b9ae7ecd2dc6b10f81d Mon Sep 17 00:00:00 2001 From: Enrico Giordani Date: Tue, 22 Sep 2020 13:12:19 +0200 Subject: [PATCH 06/22] [SG] change suppress key code (#6750) --- src/modules/shortcut_guide/target_state.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/modules/shortcut_guide/target_state.cpp b/src/modules/shortcut_guide/target_state.cpp index 8cb81fe3eb..bf0f14a125 100644 --- a/src/modules/shortcut_guide/target_state.cpp +++ b/src/modules/shortcut_guide/target_state.cpp @@ -39,16 +39,14 @@ bool TargetState::signal_event(unsigned vk_code, bool key_down) cv.notify_one(); if (suppress_win_release) { - // Send a fake key-stroke to prevent the start menu from appearing. - // We use 0xCF VK code, which is reserved. It still prevents the - // start menu from appearing, but should not interfere with any - // keyboard shortcuts. + // Send a 0xFF VK code, which is outside of the VK code range, to prevent + // the start menu from appearing. INPUT input[3] = { {}, {}, {} }; input[0].type = INPUT_KEYBOARD; - input[0].ki.wVk = 0xCF; + input[0].ki.wVk = 0xFF; input[0].ki.dwExtraInfo = CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG; input[1].type = INPUT_KEYBOARD; - input[1].ki.wVk = 0xCF; + input[1].ki.wVk = 0xFF; input[1].ki.dwFlags = KEYEVENTF_KEYUP; input[1].ki.dwExtraInfo = CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG; input[2].type = INPUT_KEYBOARD; From 55b36d44fa8c7f37dd2da7c2a06bd37f8b7227bd Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Tue, 22 Sep 2020 09:10:18 -0700 Subject: [PATCH 07/22] adding in frontmatter and latex (#6711) --- .../MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs index 385ed0bcbc..5957756f4b 100644 --- a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs +++ b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs @@ -62,7 +62,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Markdown public MarkdownPreviewHandlerControl() { _extension = new HTMLParsingExtension(ImagesBlockedCallBack); - _pipelineBuilder = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseEmojiAndSmiley(); + _pipelineBuilder = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseEmojiAndSmiley().UseYamlFrontMatter().UseMathematics(); _pipelineBuilder.Extensions.Add(_extension); } From c1e9f2de6a27541778f7a06e2015c5e80c0b942f Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Tue, 22 Sep 2020 09:14:42 -0700 Subject: [PATCH 08/22] FxCop enabled (#6758) --- .../HTMLParsingExtensionTest.cs | 13 +++++++------ .../MarkdownPreviewHandlerTest.cs | 12 ++++++------ .../UnitTests-MarkdownPreviewHandler.csproj | 5 +++++ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/modules/previewpane/PreviewPaneUnitTests/HTMLParsingExtensionTest.cs b/src/modules/previewpane/PreviewPaneUnitTests/HTMLParsingExtensionTest.cs index 1421d0e776..f632d988fa 100644 --- a/src/modules/previewpane/PreviewPaneUnitTests/HTMLParsingExtensionTest.cs +++ b/src/modules/previewpane/PreviewPaneUnitTests/HTMLParsingExtensionTest.cs @@ -11,15 +11,16 @@ namespace PreviewPaneUnitTests [TestClass] public class HTMLParsingExtensionTest { - private MarkdownPipeline BuidPipeline(IMarkdownExtension extension) + private static MarkdownPipeline BuidPipeline(IMarkdownExtension extension) { MarkdownPipelineBuilder pipelineBuilder = new MarkdownPipelineBuilder().UseAdvancedExtensions(); pipelineBuilder.Extensions.Add(extension); + return pipelineBuilder.Build(); } [TestMethod] - public void Extension_UpdatesTablesClass_WhenUsed() + public void ExtensionUpdatesTablesClassWhenUsed() { // Arrange string mdString = "| A | B |\n| -- | -- | "; @@ -34,7 +35,7 @@ namespace PreviewPaneUnitTests } [TestMethod] - public void Extension_UpdatesBlockQuotesClass_WhenUsed() + public void ExtensionUpdatesBlockQuotesClassWhenUsed() { // Arrange string mdString = "> Blockquotes."; @@ -49,7 +50,7 @@ namespace PreviewPaneUnitTests } [TestMethod] - public void Extension_UpdatesFigureClassAndBlocksRelativeUrl_WhenUsed() + public void ExtensionUpdatesFigureClassAndBlocksRelativeUrlWhenUsed() { // arrange string mdString = "![text](a.jpg \"Figure\")"; @@ -64,7 +65,7 @@ namespace PreviewPaneUnitTests } [TestMethod] - public void Extension_AddsClassToFigureCaption_WhenUsed() + public void ExtensionAddsClassToFigureCaptionWhenUsed() { // arrange string mdString = "^^^ This is a caption"; @@ -79,7 +80,7 @@ namespace PreviewPaneUnitTests } [TestMethod] - public void Extension_RemovesExternalImageUrlAndMakeCallback_WhenUsed() + public void ExtensionRemovesExternalImageUrlAndMakeCallbackWhenUsed() { // arrange int count = 0; diff --git a/src/modules/previewpane/PreviewPaneUnitTests/MarkdownPreviewHandlerTest.cs b/src/modules/previewpane/PreviewPaneUnitTests/MarkdownPreviewHandlerTest.cs index 9e0db75cd3..1efed2aa50 100644 --- a/src/modules/previewpane/PreviewPaneUnitTests/MarkdownPreviewHandlerTest.cs +++ b/src/modules/previewpane/PreviewPaneUnitTests/MarkdownPreviewHandlerTest.cs @@ -14,7 +14,7 @@ namespace PreviewPaneUnitTests public class MarkdownPreviewHandlerTest { [TestMethod] - public void MarkdownPreviewHandlerControl__AddsBrowserToForm_WhenDoPreviewIsCalled() + public void MarkdownPreviewHandlerControlAddsBrowserToFormWhenDoPreviewIsCalled() { // Arrange using (var markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl()) @@ -29,7 +29,7 @@ namespace PreviewPaneUnitTests } [TestMethod] - public void MarkdownPreviewHandlerControl__AddsInfoBarToFormIfExternalImageLinkPresent_WhenDoPreviewIsCalled() + public void MarkdownPreviewHandlerControlAddsInfoBarToFormIfExternalImageLinkPresentWhenDoPreviewIsCalled() { // Arrange using (var markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl()) @@ -44,7 +44,7 @@ namespace PreviewPaneUnitTests } [TestMethod] - public void MarkdownPreviewHandlerControl__AddsInfoBarToFormIfHTMLImageTagIsPresent_WhenDoPreviewIsCalled() + public void MarkdownPreviewHandlerControlAddsInfoBarToFormIfHTMLImageTagIsPresentWhenDoPreviewIsCalled() { // Arrange using (var markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl()) @@ -59,7 +59,7 @@ namespace PreviewPaneUnitTests } [TestMethod] - public void MarkdownPreviewHandlerControl__DoesNotAddInfoBarToFormIfExternalImageLinkNotPresent_WhenDoPreviewIsCalled() + public void MarkdownPreviewHandlerControlDoesNotAddInfoBarToFormIfExternalImageLinkNotPresentWhenDoPreviewIsCalled() { // Arrange using (var markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl()) @@ -74,7 +74,7 @@ namespace PreviewPaneUnitTests } [TestMethod] - public void MarkdownPreviewHandlerControl__UpdatesWebBrowserSettings_WhenDoPreviewIsCalled() + public void MarkdownPreviewHandlerControlUpdatesWebBrowserSettingsWhenDoPreviewIsCalled() { // Arrange using (var markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl()) @@ -94,7 +94,7 @@ namespace PreviewPaneUnitTests } [TestMethod] - public void MarkdownPreviewHandlerControl__UpdateInfobarSettings_WhenDoPreviewIsCalled() + public void MarkdownPreviewHandlerControlUpdateInfobarSettingsWhenDoPreviewIsCalled() { // Arrange using (var markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl()) diff --git a/src/modules/previewpane/PreviewPaneUnitTests/UnitTests-MarkdownPreviewHandler.csproj b/src/modules/previewpane/PreviewPaneUnitTests/UnitTests-MarkdownPreviewHandler.csproj index 20f41dc70e..38420cddae 100644 --- a/src/modules/previewpane/PreviewPaneUnitTests/UnitTests-MarkdownPreviewHandler.csproj +++ b/src/modules/previewpane/PreviewPaneUnitTests/UnitTests-MarkdownPreviewHandler.csproj @@ -102,6 +102,11 @@ 0.21.1 + + 3.3.0 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + 2.1.2 From 1ef4c43382e4de735d4702a63fc96cc50955e245 Mon Sep 17 00:00:00 2001 From: Arjun Balgovind <32061677+arjunbalgovind@users.noreply.github.com> Date: Tue, 22 Sep 2020 13:54:53 -0700 Subject: [PATCH 09/22] [Keyboard Manager] Fix crash in Keyboard Manager by ignoring MapToSameKey warnings on loading (#6714) * Add extra argument to ignore MapToSameKey error for Key To Shortcut mapping * added comment --- .../keyboardmanager/ui/KeyDropDownControl.cpp | 28 +++++++++++++++++-- .../keyboardmanager/ui/KeyDropDownControl.h | 7 +++-- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp b/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp index 9bd6568783..6b42ae929a 100644 --- a/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp +++ b/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp @@ -148,6 +148,13 @@ std::pair KeyDropDownControl::ValidateSho parent.UpdateLayout(); } + // If ignore key to shortcut warning flag is true and it is a hybrid control in SingleKeyRemapControl, then skip MapToSameKey error + if (isHybridControl && isSingleKeyWindow && ignoreKeyToShortcutWarning && (validationResult.first == KeyboardManagerHelper::ErrorType::MapToSameKey)) + { + validationResult.first = KeyboardManagerHelper::ErrorType::NoError; + } + + // If the remapping is invalid display an error message if (validationResult.first != KeyboardManagerHelper::ErrorType::NoError) { SetDropDownError(currentDropDown, KeyboardManagerHelper::GetErrorMessage(validationResult.first)); @@ -170,6 +177,12 @@ std::pair KeyDropDownControl::ValidateSho } } + // Reset ignoreKeyToShortcutWarning + if (ignoreKeyToShortcutWarning) + { + ignoreKeyToShortcutWarning = false; + } + return std::make_pair(validationResult.first, rowIndex); } @@ -268,9 +281,9 @@ ComboBox KeyDropDownControl::GetComboBox() } // Function to add a drop down to the shortcut stack panel -void KeyDropDownControl::AddDropDown(Grid table, StackPanel shortcutControl, StackPanel parent, const int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow) +void KeyDropDownControl::AddDropDown(Grid table, StackPanel shortcutControl, StackPanel parent, const int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, bool ignoreWarning) { - keyDropDownControlObjects.push_back(std::move(std::unique_ptr(new KeyDropDownControl(true)))); + keyDropDownControlObjects.push_back(std::move(std::unique_ptr(new KeyDropDownControl(true, ignoreWarning)))); parent.Children().Append(keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->GetComboBox()); keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->SetSelectionHandler(table, shortcutControl, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow); parent.UpdateLayout(); @@ -342,7 +355,16 @@ void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, Grid table, Sta std::vector keyCodeList = keyboardManagerState.keyboardMap.GetKeyCodeList(true); if (shortcutKeyCodes.size() != 0) { - KeyDropDownControl::AddDropDown(table, controlLayout, parent, colIndex, remapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow); + bool ignoreWarning = false; + + // If more than one key is to be added, ignore a shortcut to key warning on partially entering the remapping + if (shortcutKeyCodes.size() > 1) + { + ignoreWarning = true; + } + + KeyDropDownControl::AddDropDown(table, controlLayout, parent, colIndex, remapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow, ignoreWarning); + for (int i = 0; i < shortcutKeyCodes.size(); i++) { // New drop down gets added automatically when the SelectedIndex is set diff --git a/src/modules/keyboardmanager/ui/KeyDropDownControl.h b/src/modules/keyboardmanager/ui/KeyDropDownControl.h index 3d3c13961b..f50d37cfb4 100644 --- a/src/modules/keyboardmanager/ui/KeyDropDownControl.h +++ b/src/modules/keyboardmanager/ui/KeyDropDownControl.h @@ -38,6 +38,8 @@ private: winrt::Windows::Foundation::IInspectable warningMessage; // Stores the flyout attached to the current drop down winrt::Windows::Foundation::IInspectable warningFlyout; + // Stores whether a key to shortcut warning has to be ignored + bool ignoreKeyToShortcutWarning; // Function to set properties apart from the SelectionChanged event handler void SetDefaultProperties(bool isShortcut); @@ -53,7 +55,8 @@ public: static KeyboardManagerState* keyboardManagerState; // Constructor - the last default parameter should be passed as false only if it originates from Type shortcut or when an old shortcut is reloaded - KeyDropDownControl(bool isShortcut) + KeyDropDownControl(bool isShortcut, bool fromAddShortcutToControl = false) : + ignoreKeyToShortcutWarning(fromAddShortcutToControl) { SetDefaultProperties(isShortcut); } @@ -74,7 +77,7 @@ public: ComboBox GetComboBox(); // Function to add a drop down to the shortcut stack panel - static void AddDropDown(winrt::Windows::UI::Xaml::Controls::Grid table, winrt::Windows::UI::Xaml::Controls::StackPanel shortcutControl, winrt::Windows::UI::Xaml::Controls::StackPanel parent, const int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow); + static void AddDropDown(winrt::Windows::UI::Xaml::Controls::Grid table, winrt::Windows::UI::Xaml::Controls::StackPanel shortcutControl, winrt::Windows::UI::Xaml::Controls::StackPanel parent, const int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, bool ignoreWarning = false); // Function to get the list of key codes from the shortcut combo box stack panel static std::vector GetSelectedIndicesFromStackPanel(StackPanel parent); From 75ace74d37f3e45be96d85a81b01bead54f9e2ac Mon Sep 17 00:00:00 2001 From: Arjun Balgovind <32061677+arjunbalgovind@users.noreply.github.com> Date: Tue, 22 Sep 2020 17:46:57 -0700 Subject: [PATCH 10/22] [Keyboard Manager] Enabled horizontal scroll on scroll viewers and tweaked ScrollViewer layout (#6755) * Enabled horizontal scroll on scroll viewers and tweaked layout * Added code to constrain the minimum window sizes --- .../common/KeyboardManagerConstants.h | 6 +++ .../keyboardmanager/ui/EditKeyboardWindow.cpp | 37 ++++++++++++++++--- .../ui/EditShortcutsWindow.cpp | 35 ++++++++++++++++-- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/modules/keyboardmanager/common/KeyboardManagerConstants.h b/src/modules/keyboardmanager/common/KeyboardManagerConstants.h index 77dd64797b..07a2a1ace2 100644 --- a/src/modules/keyboardmanager/common/KeyboardManagerConstants.h +++ b/src/modules/keyboardmanager/common/KeyboardManagerConstants.h @@ -53,8 +53,14 @@ namespace KeyboardManagerConstants // Default window sizes inline const int DefaultEditKeyboardWindowWidth = 800; inline const int DefaultEditKeyboardWindowHeight = 600; + inline const int MinimumEditKeyboardWindowWidth = 500; + inline const int MinimumEditKeyboardWindowHeight = 450; + inline const int EditKeyboardTableMinWidth = 700; inline const int DefaultEditShortcutsWindowWidth = 1050; inline const int DefaultEditShortcutsWindowHeight = 600; + inline const int MinimumEditShortcutsWindowWidth = 500; + inline const int MinimumEditShortcutsWindowHeight = 500; + inline const int EditShortcutsTableMinWidth = 1000; // Key Remap table constants inline const long RemapTableColCount = 4; diff --git a/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp b/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp index 3facc45432..1c2b5119c5 100644 --- a/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp +++ b/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp @@ -216,6 +216,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan keyRemapTable.ColumnDefinitions().Append(newColumn); keyRemapTable.ColumnDefinitions().Append(removeColumn); keyRemapTable.RowDefinitions().Append(RowDefinition()); + keyRemapTable.MinWidth(KeyboardManagerConstants::EditKeyboardTableMinWidth); // First header textblock in the header row of the keys remap table TextBlock originalKeyRemapHeader; @@ -286,6 +287,10 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan header.Children().Append(cancelButton); ScrollViewer scrollViewer; + scrollViewer.VerticalScrollMode(ScrollMode::Enabled); + scrollViewer.HorizontalScrollMode(ScrollMode::Enabled); + scrollViewer.VerticalScrollBarVisibility(ScrollBarVisibility::Auto); + scrollViewer.HorizontalScrollBarVisibility(ScrollBarVisibility::Auto); // Add remap key button Windows::UI::Xaml::Controls::Button addRemapKey; @@ -293,7 +298,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan plusSymbol.FontFamily(Media::FontFamily(L"Segoe MDL2 Assets")); plusSymbol.Glyph(L"\xE109"); addRemapKey.Content(plusSymbol); - addRemapKey.Margin({ 10, 0, 0, 25 }); + addRemapKey.Margin({ 10, 10, 0, 25 }); addRemapKey.Click([&](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { SingleKeyRemapControl::AddNewControlKeyRemapRow(keyRemapTable, keyboardRemapControlObjects); // Whenever a remap is added move to the bottom of the screen @@ -302,22 +307,31 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan // Set accessible name for the addRemapKey button addRemapKey.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_ADD_KEY_REMAP_BUTTON))); + // Header and example text at the top of the window + StackPanel helperText; + helperText.Children().Append(keyRemapInfoHeader); + helperText.Children().Append(keyRemapInfoExample); + + // Remapping table StackPanel mappingsPanel; - mappingsPanel.Children().Append(keyRemapInfoHeader); - mappingsPanel.Children().Append(keyRemapInfoExample); mappingsPanel.Children().Append(keyRemapTable); mappingsPanel.Children().Append(addRemapKey); + // Remapping table should be scrollable scrollViewer.Content(mappingsPanel); // Creating the Xaml content. xamlContainer is the parent UI element RelativePanel xamlContainer; - xamlContainer.SetBelow(scrollViewer, header); + xamlContainer.SetBelow(helperText, header); + xamlContainer.SetBelow(scrollViewer, helperText); xamlContainer.SetAlignLeftWithPanel(header, true); xamlContainer.SetAlignRightWithPanel(header, true); + xamlContainer.SetAlignLeftWithPanel(helperText, true); + xamlContainer.SetAlignRightWithPanel(helperText, true); xamlContainer.SetAlignLeftWithPanel(scrollViewer, true); xamlContainer.SetAlignRightWithPanel(scrollViewer, true); xamlContainer.Children().Append(header); + xamlContainer.Children().Append(helperText); xamlContainer.Children().Append(scrollViewer); xamlContainer.UpdateLayout(); @@ -352,9 +366,22 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wPar // Resize the XAML window whenever the parent window is painted or resized case WM_PAINT: case WM_SIZE: + { GetClientRect(hWnd, &rcClient); SetWindowPos(hWndXamlIslandEditKeyboardWindow, 0, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom, SWP_SHOWWINDOW); - break; + } + break; + // To avoid UI elements overlapping on making the window smaller enforce a minimum window size + case WM_GETMINMAXINFO: + { + LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam; + int minWidth = KeyboardManagerConstants::MinimumEditKeyboardWindowWidth; + int minHeight = KeyboardManagerConstants::MinimumEditKeyboardWindowHeight; + DPIAware::Convert(nullptr, minWidth, minHeight); + lpMMI->ptMinTrackSize.x = minWidth; + lpMMI->ptMinTrackSize.y = minHeight; + } + break; default: // If the Xaml Bridge object exists, then use it's message handler to handle keyboard focus operations if (xamlBridgePtr != nullptr) diff --git a/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp b/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp index f8c9f99982..bb8cde6ece 100644 --- a/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp +++ b/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp @@ -179,6 +179,7 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa shortcutTable.ColumnDefinitions().Append(targetAppColumn); shortcutTable.ColumnDefinitions().Append(removeColumn); shortcutTable.RowDefinitions().Append(RowDefinition()); + shortcutTable.MinWidth(KeyboardManagerConstants::EditShortcutsTableMinWidth); // First header textblock in the header row of the shortcut table TextBlock originalShortcutHeader; @@ -270,6 +271,10 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa header.Children().Append(cancelButton); ScrollViewer scrollViewer; + scrollViewer.VerticalScrollMode(ScrollMode::Enabled); + scrollViewer.HorizontalScrollMode(ScrollMode::Enabled); + scrollViewer.VerticalScrollBarVisibility(ScrollBarVisibility::Auto); + scrollViewer.HorizontalScrollBarVisibility(ScrollBarVisibility::Auto); // Add shortcut button Windows::UI::Xaml::Controls::Button addShortcut; @@ -286,21 +291,30 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa // Set accessible name for the add shortcut button addShortcut.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_ADD_SHORTCUT_BUTTON))); + // Header and example text at the top of the window + StackPanel helperText; + helperText.Children().Append(shortcutRemapInfoHeader); + helperText.Children().Append(shortcutRemapInfoExample); + + // Remapping table StackPanel mappingsPanel; - mappingsPanel.Children().Append(shortcutRemapInfoHeader); - mappingsPanel.Children().Append(shortcutRemapInfoExample); mappingsPanel.Children().Append(shortcutTable); mappingsPanel.Children().Append(addShortcut); + // Remapping table should be scrollable scrollViewer.Content(mappingsPanel); RelativePanel xamlContainer; - xamlContainer.SetBelow(scrollViewer, header); + xamlContainer.SetBelow(helperText, header); + xamlContainer.SetBelow(scrollViewer, helperText); xamlContainer.SetAlignLeftWithPanel(header, true); xamlContainer.SetAlignRightWithPanel(header, true); + xamlContainer.SetAlignLeftWithPanel(helperText, true); + xamlContainer.SetAlignRightWithPanel(helperText, true); xamlContainer.SetAlignLeftWithPanel(scrollViewer, true); xamlContainer.SetAlignRightWithPanel(scrollViewer, true); xamlContainer.Children().Append(header); + xamlContainer.Children().Append(helperText); xamlContainer.Children().Append(scrollViewer); xamlContainer.UpdateLayout(); @@ -336,9 +350,22 @@ LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wPa // Resize the XAML window whenever the parent window is painted or resized case WM_PAINT: case WM_SIZE: + { GetClientRect(hWnd, &rcClient); SetWindowPos(hWndXamlIslandEditShortcutsWindow, 0, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom, SWP_SHOWWINDOW); - break; + } + break; + // To avoid UI elements overlapping on making the window smaller enforce a minimum window size + case WM_GETMINMAXINFO: + { + LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam; + int minWidth = KeyboardManagerConstants::MinimumEditShortcutsWindowWidth; + int minHeight = KeyboardManagerConstants::MinimumEditShortcutsWindowHeight; + DPIAware::Convert(nullptr, minWidth, minHeight); + lpMMI->ptMinTrackSize.x = minWidth; + lpMMI->ptMinTrackSize.y = minHeight; + } + break; default: // If the Xaml Bridge object exists, then use it's message handler to handle keyboard focus operations if (xamlBridgePtr != nullptr) From 0148669e98abf96dd8eb4d8737e72e063a747178 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Wed, 23 Sep 2020 10:22:17 -0700 Subject: [PATCH 11/22] [fxcop] preview handler common (#6762) * FxCop adjustments * initing due to change for abstract --- .../MarkdownPreviewHandler.cs | 8 +++ .../SvgPreviewHandler/SvgPreviewControl.cs | 2 +- .../SvgPreviewHandler/SvgPreviewHandler.cs | 8 +++ .../SvgThumbnailProvider.cs | 2 +- .../FormHandlerControlTests.cs | 2 +- .../PreviewHandlerBaseTests.cs | 7 ++- .../StreamWrapperTests.cs | 28 ++++----- .../common/PreviewHandlerCommon.csproj | 8 ++- .../{StreamWrapper.cs => ReadonlyStream.cs} | 52 ++++++++------- .../previewpane/common/cominterop/COLORREF.cs | 11 ++-- .../common/cominterop/IThumbnailProvider.cs | 3 +- .../previewpane/common/cominterop/LOGFONT.cs | 63 ++++++++++--------- .../previewpane/common/cominterop/MSG.cs | 29 ++++----- .../common/cominterop/NativeMethods.cs | 37 +++++++++++ .../previewpane/common/cominterop/RECT.cs | 19 +++--- .../common/controls/FormHandlerControl.cs | 36 +++-------- .../common/controls/IPreviewHandlerControl.cs | 2 +- .../WebBrowserDownloadControlFlags.cs | 1 + .../common/controls/WebBrowserExt.cs | 7 ++- .../examplehandler/CustomControlTest.cs | 3 +- .../examplehandler/TestCustomHandler.cs | 40 ++++++++++-- .../common/handlers/PreviewHandlerBase.cs | 11 +++- 22 files changed, 241 insertions(+), 138 deletions(-) rename src/modules/previewpane/common/Utilities/{StreamWrapper.cs => ReadonlyStream.cs} (83%) create mode 100644 src/modules/previewpane/common/cominterop/NativeMethods.cs diff --git a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.cs b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.cs index 156e635f54..6e9504831b 100644 --- a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.cs +++ b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.cs @@ -20,6 +20,14 @@ namespace Microsoft.PowerToys.PreviewHandler.Markdown private MarkdownPreviewHandlerControl _markdownPreviewHandlerControl; private bool disposedValue; + /// + /// Initializes a new instance of the class. + /// + public MarkdownPreviewHandler() + { + Initialize(); + } + /// public override void DoPreview() { diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs index 9a78ae7a98..21db1eda68 100644 --- a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs +++ b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs @@ -48,7 +48,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg { _infoBarAdded = false; string svgData = null; - using (var stream = new StreamWrapper(dataSource as IStream)) + using (var stream = new ReadonlyStream(dataSource as IStream)) { using (var reader = new StreamReader(stream)) { diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.cs b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.cs index 08d6519d73..dbf0a340ad 100644 --- a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.cs +++ b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.cs @@ -20,6 +20,14 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg private SvgPreviewControl _svgPreviewControl; private bool disposedValue; + /// + /// Initializes a new instance of the class. + /// + public SvgPreviewHandler() + { + Initialize(); + } + /// public override void DoPreview() { diff --git a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs index 925091ae7b..52f52aa00c 100644 --- a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs +++ b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.cs @@ -225,7 +225,7 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg } string svgData = null; - using (var stream = new StreamWrapper(this.Stream as IStream)) + using (var stream = new ReadonlyStream(this.Stream as IStream)) { using (var reader = new StreamReader(stream)) { diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs index e539d9e43b..5fbde64d1f 100644 --- a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs @@ -58,7 +58,7 @@ namespace UnitTests_PreviewHandlerCommon using (var testFormHandlerControl = new TestFormControl()) { // Act - var handle = testFormHandlerControl.GetHandle(); + var handle = testFormHandlerControl.Handle; // Assert Assert.AreEqual(testFormHandlerControl.Handle, handle); diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/PreviewHandlerBaseTests.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/PreviewHandlerBaseTests.cs index 6dba4ffa19..b7105b13c5 100644 --- a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/PreviewHandlerBaseTests.cs +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/PreviewHandlerBaseTests.cs @@ -19,6 +19,11 @@ namespace UnitTests_PreviewHandlerCommon public class TestPreviewHandler : PreviewHandlerBase { + public TestPreviewHandler() + { + Initialize(); + } + public override void DoPreview() { throw new NotImplementedException(); @@ -291,7 +296,7 @@ namespace UnitTests_PreviewHandlerCommon // Arrange var previewControlHandle = new IntPtr(5); var mockPreviewControl = new Mock(); - mockPreviewControl.Setup(x => x.GetHandle()) + mockPreviewControl.Setup(x => x.GetWindowHandle()) .Returns(previewControlHandle); previewHandlerControl = mockPreviewControl.Object; diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/StreamWrapperTests.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/StreamWrapperTests.cs index db7d13deba..5b9395a7e0 100644 --- a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/StreamWrapperTests.cs +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/StreamWrapperTests.cs @@ -26,7 +26,7 @@ namespace UnitTests_PreviewHandlerCommon // Act try { - var streamWrapper = new StreamWrapper(stream); + var streamWrapper = new ReadonlyStream(stream); } catch (ArgumentNullException ex) { @@ -44,7 +44,7 @@ namespace UnitTests_PreviewHandlerCommon var streamMock = new Mock(); // Act - var streamWrapper = new StreamWrapper(streamMock.Object); + var streamWrapper = new ReadonlyStream(streamMock.Object); // Assert Assert.AreEqual(streamWrapper.CanRead, true); @@ -57,7 +57,7 @@ namespace UnitTests_PreviewHandlerCommon var streamMock = new Mock(); // Act - var streamWrapper = new StreamWrapper(streamMock.Object); + var streamWrapper = new ReadonlyStream(streamMock.Object); // Assert Assert.AreEqual(streamWrapper.CanSeek, true); @@ -70,7 +70,7 @@ namespace UnitTests_PreviewHandlerCommon var streamMock = new Mock(); // Act - var streamWrapper = new StreamWrapper(streamMock.Object); + var streamWrapper = new ReadonlyStream(streamMock.Object); // Assert Assert.AreEqual(streamWrapper.CanWrite, false); @@ -89,7 +89,7 @@ namespace UnitTests_PreviewHandlerCommon stremMock .Setup(x => x.Stat(out stat, It.IsAny())); - var streamWrapper = new StreamWrapper(stremMock.Object); + var streamWrapper = new ReadonlyStream(stremMock.Object); // Act var actualLength = streamWrapper.Length; @@ -113,7 +113,7 @@ namespace UnitTests_PreviewHandlerCommon { Marshal.WriteInt64(plibNewPosition, currPosition); }); - var streamWrapper = new StreamWrapper(stremMock.Object); + var streamWrapper = new ReadonlyStream(stremMock.Object); // Act var actualPosition = streamWrapper.Position; @@ -131,7 +131,7 @@ namespace UnitTests_PreviewHandlerCommon int expectedDwOrigin = 0; // STREAM_SEEK_SET var stremMock = new Mock(); - var streamWrapper = new StreamWrapper(stremMock.Object) + var streamWrapper = new ReadonlyStream(stremMock.Object) { // Act Position = positionToSet, @@ -168,7 +168,7 @@ namespace UnitTests_PreviewHandlerCommon } var stremMock = new Mock(); - var streamWrapper = new StreamWrapper(stremMock.Object); + var streamWrapper = new ReadonlyStream(stremMock.Object); // Act streamWrapper.Seek(offset, origin); @@ -191,7 +191,7 @@ namespace UnitTests_PreviewHandlerCommon Marshal.WriteInt64(plibNewPosition, position); }); - var streamWrapper = new StreamWrapper(stremMock.Object); + var streamWrapper = new ReadonlyStream(stremMock.Object); // Act var actualPosition = streamWrapper.Seek(0, SeekOrigin.Begin); @@ -212,7 +212,7 @@ namespace UnitTests_PreviewHandlerCommon var stremMock = new Mock(); ArgumentOutOfRangeException exception = null; - var streamWrapper = new StreamWrapper(stremMock.Object); + var streamWrapper = new ReadonlyStream(stremMock.Object); // Act try @@ -252,7 +252,7 @@ namespace UnitTests_PreviewHandlerCommon Marshal.WriteInt32(bytesReadPtr, count); }); - var streamWrapper = new StreamWrapper(stremMock.Object); + var streamWrapper = new ReadonlyStream(stremMock.Object); // Act var bytesRead = streamWrapper.Read(inputBuffer, offset, count); @@ -267,7 +267,7 @@ namespace UnitTests_PreviewHandlerCommon { // Arrange var stremMock = new Mock(); - var streamWrapper = new StreamWrapper(stremMock.Object); + var streamWrapper = new ReadonlyStream(stremMock.Object); NotImplementedException exception = null; // Act @@ -289,7 +289,7 @@ namespace UnitTests_PreviewHandlerCommon { // Arrange var stremMock = new Mock(); - var streamWrapper = new StreamWrapper(stremMock.Object); + var streamWrapper = new ReadonlyStream(stremMock.Object); NotImplementedException exception = null; // Act @@ -311,7 +311,7 @@ namespace UnitTests_PreviewHandlerCommon { // Arrange var stremMock = new Mock(); - var streamWrapper = new StreamWrapper(stremMock.Object); + var streamWrapper = new ReadonlyStream(stremMock.Object); NotImplementedException exception = null; // Act diff --git a/src/modules/previewpane/common/PreviewHandlerCommon.csproj b/src/modules/previewpane/common/PreviewHandlerCommon.csproj index f8adb7a9a3..eae65aae56 100644 --- a/src/modules/previewpane/common/PreviewHandlerCommon.csproj +++ b/src/modules/previewpane/common/PreviewHandlerCommon.csproj @@ -110,6 +110,7 @@ + @@ -129,7 +130,7 @@ - + @@ -137,6 +138,11 @@ + + 3.3.0 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + 1.1.118 runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/modules/previewpane/common/Utilities/StreamWrapper.cs b/src/modules/previewpane/common/Utilities/ReadonlyStream.cs similarity index 83% rename from src/modules/previewpane/common/Utilities/StreamWrapper.cs rename to src/modules/previewpane/common/Utilities/ReadonlyStream.cs index c1f8eb6acd..a9ae949181 100644 --- a/src/modules/previewpane/common/Utilities/StreamWrapper.cs +++ b/src/modules/previewpane/common/Utilities/ReadonlyStream.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; @@ -15,17 +16,17 @@ namespace Common.Utilities /// /// Implements only read from the stream functionality. /// - public class StreamWrapper : Stream + public class ReadonlyStream : Stream { - private IStream stream; + private IStream _stream; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// A pointer to an interface that represents the stream source. - public StreamWrapper(IStream stream) + public ReadonlyStream(IStream stream) { - this.stream = stream ?? throw new ArgumentNullException(nameof(stream)); + _stream = stream ?? throw new ArgumentNullException(nameof(stream)); } /// @@ -68,11 +69,11 @@ namespace Common.Utilities { get { - this.CheckDisposed(); + CheckDisposed(); System.Runtime.InteropServices.ComTypes.STATSTG stat; // Stat called with STATFLAG_NONAME. The pwcsName is not required more details https://docs.microsoft.com/en-us/windows/win32/api/wtypes/ne-wtypes-statflag - this.stream.Stat(out stat, 1); // STATFLAG_NONAME + _stream.Stat(out stat, 1); // STATFLAG_NONAME return stat.cbSize; } @@ -85,12 +86,12 @@ namespace Common.Utilities { get { - return this.Seek(0, SeekOrigin.Current); + return Seek(0, SeekOrigin.Current); } set { - this.Seek(value, SeekOrigin.Begin); + Seek(value, SeekOrigin.Begin); } } @@ -103,11 +104,16 @@ namespace Common.Utilities /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero if the end of the stream has been reached. public override int Read(byte[] buffer, int offset, int count) { - this.CheckDisposed(); + CheckDisposed(); - if (offset < 0 || count < 0 || offset + count > buffer.Length) + if (buffer == null) { - throw new ArgumentOutOfRangeException(); + throw new NullReferenceException("buffer is null"); + } + + if (offset < 0 || count < 0 || (offset + count) > buffer.Length) + { + throw new ArgumentOutOfRangeException(nameof(buffer), "Out of range for buffer"); } byte[] localBuffer = buffer; @@ -121,7 +127,7 @@ namespace Common.Utilities try { - this.stream.Read(localBuffer, count, bytesReadPtr); + _stream.Read(localBuffer, count, bytesReadPtr); int bytesRead = Marshal.ReadInt32(bytesReadPtr); if (offset > 0) @@ -145,7 +151,7 @@ namespace Common.Utilities /// The new position within the current stream. public override long Seek(long offset, SeekOrigin origin) { - this.CheckDisposed(); + CheckDisposed(); int dwOrigin; // Maps the SeekOrigin with dworigin more details: https://docs.microsoft.com/en-us/windows/win32/api/objidl/ne-objidl-stream_seek @@ -164,14 +170,14 @@ namespace Common.Utilities break; default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(origin)); } IntPtr posPtr = Marshal.AllocCoTaskMem(sizeof(long)); try { - this.stream.Seek(offset, dwOrigin, posPtr); + _stream.Seek(offset, dwOrigin, posPtr); return Marshal.ReadInt64(posPtr); } finally @@ -220,22 +226,24 @@ namespace Common.Utilities /// protected override void Dispose(bool disposing) { - if (this.stream != null) + base.Dispose(true); + + if (_stream != null) { - if (Marshal.IsComObject(this.stream)) + if (Marshal.IsComObject(_stream)) { - Marshal.ReleaseComObject(this.stream); + Marshal.ReleaseComObject(_stream); } - this.stream = null; + _stream = null; } } private void CheckDisposed() { - if (this.stream == null) + if (_stream == null) { - throw new ObjectDisposedException(nameof(StreamWrapper)); + throw new ObjectDisposedException(nameof(ReadonlyStream)); } } } diff --git a/src/modules/previewpane/common/cominterop/COLORREF.cs b/src/modules/previewpane/common/cominterop/COLORREF.cs index 65d1216973..0edf457f0f 100644 --- a/src/modules/previewpane/common/cominterop/COLORREF.cs +++ b/src/modules/previewpane/common/cominterop/COLORREF.cs @@ -11,12 +11,13 @@ namespace Common.ComInterlop /// The COLORREF value is used to specify an RGB color. /// [StructLayout(LayoutKind.Sequential)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Interop")] public struct COLORREF { /// - /// Stores an RGB color value in a 32 bit integer. + /// Gets or sets stores an RGB color value in a 32 bit integer. /// - public uint Dword; + public uint Dword { get; set; } /// /// Gets RGB value stored in in structure. @@ -26,9 +27,9 @@ namespace Common.ComInterlop get { return Color.FromArgb( - (int)(0x000000FFU & this.Dword), - (int)(0x0000FF00U & this.Dword) >> 8, - (int)(0x00FF0000U & this.Dword) >> 16); + (int)(0x000000FFU & Dword), + (int)(0x0000FF00U & Dword) >> 8, + (int)(0x00FF0000U & Dword) >> 16); } } } diff --git a/src/modules/previewpane/common/cominterop/IThumbnailProvider.cs b/src/modules/previewpane/common/cominterop/IThumbnailProvider.cs index 758d231c51..78a6a866ff 100644 --- a/src/modules/previewpane/common/cominterop/IThumbnailProvider.cs +++ b/src/modules/previewpane/common/cominterop/IThumbnailProvider.cs @@ -10,10 +10,11 @@ namespace Common.ComInterlop /// /// Specifies the alpha type of the image. /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Interop")] public enum WTS_ALPHATYPE : int { /// - /// he bitmap is an unknown format. The Shell tries nonetheless to detect whether the image has an alpha channel. + /// The bitmap is an unknown format. The Shell tries nonetheless to detect whether the image has an alpha channel. /// WTSAT_UNKNOWN = 0, diff --git a/src/modules/previewpane/common/cominterop/LOGFONT.cs b/src/modules/previewpane/common/cominterop/LOGFONT.cs index e3b1e88d51..a20cedf11b 100644 --- a/src/modules/previewpane/common/cominterop/LOGFONT.cs +++ b/src/modules/previewpane/common/cominterop/LOGFONT.cs @@ -10,79 +10,86 @@ namespace Common.ComInterlop /// Defines the attributes of a font. /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Interop")] public struct LOGFONT { /// - /// Value of type INT that specifies the height, in logical units, of the font's character cell or character. + /// Gets or sets value of type INT that specifies the height, in logical units, of the font's character cell or character. /// - public int LfHeight; + public int LfHeight { get; set; } /// - /// Value of type INT that specifies the width, in logical units, of characters in the font. + /// Gets or sets value of type INT that specifies the width, in logical units, of characters in the font. /// - public int LfWidth; + public int LfWidth { get; set; } /// - /// Value of type INT that contains the angle, in tenths of degrees, between the escapement vector and the x-axis of the device. The escapement + /// Gets or sets value of type INT that contains the angle, in tenths of degrees, between the escapement vector and the x-axis of the device. The escapement /// vector is parallel to the base line of a row of text. /// - public int LfEscapement; + public int LfEscapement { get; set; } /// - /// Value of type INT that specifies the angle, in tenths of degrees, between each character's base line and the x-axis of the device. + /// Gets or sets value of type INT that specifies the angle, in tenths of degrees, between each character's base line and the x-axis of the device. /// - public int LfOrientation; + public int LfOrientation { get; set; } /// - /// Value of type INT that specifies the weight of the font in the range from 0 through 1000. + /// Gets or sets value of type INT that specifies the weight of the font in the range from 0 through 1000. /// - public int LfWeight; + public int LfWeight { get; set; } /// - /// Value of type BYTE that specifies an italic font if set to TRUE. + /// Gets or sets value of type BYTE that specifies an italic font if set to TRUE. /// - public byte LfItalic; + public byte LfItalic { get; set; } /// - /// Value of type BYTE that specifies an underlined font if set to TRUE. + /// Gets or sets value of type BYTE that specifies an underlined font if set to TRUE. /// - public byte LfUnderline; + public byte LfUnderline { get; set; } /// - /// Value of type BYTE that specifies a strikeout font if set to TRUE. + /// Gets or sets value of type BYTE that specifies a strikeout font if set to TRUE. /// - public byte LfStrikeOut; + public byte LfStrikeOut { get; set; } /// - /// Value of type BYTE that specifies the character set. + /// Gets or sets value of type BYTE that specifies the character set. /// - public byte LfCharSet; + public byte LfCharSet { get; set; } /// - /// Value of type BYTE that specifies the output precision. The output precision defines how closely the output must match the requested + /// Gets or sets value of type BYTE that specifies the output precision. The output precision defines how closely the output must match the requested /// font's height, width, character orientation, escapement, pitch, and font type. /// - public byte LfOutPrecision; + public byte LfOutPrecision { get; set; } /// - /// Value of type BYTE that specifies the clipping precision. The clipping precision defines how to clip characters that are partially outside the clipping region. + /// Gets or sets value of type BYTE that specifies the clipping precision. The clipping precision defines how to clip characters that are partially outside the clipping region. /// - public byte LfClipPrecision; + public byte LfClipPrecision { get; set; } /// - /// Value of type BYTE that specifies the output quality. The output quality defines how carefully the GDI must attempt to match the logical-font attributes to those of an actual physical font. + /// Gets or sets value of type BYTE that specifies the output quality. The output quality defines how carefully the GDI must attempt to match the logical-font attributes to those of an actual physical font. /// - public byte LfQuality; + public byte LfQuality { get; set; } /// - /// Value of type BYTE that specifies the pitch and family of the font. + /// Gets or sets value of type BYTE that specifies the pitch and family of the font. /// - public byte LfPitchAndFamily; + public byte LfPitchAndFamily { get; set; } /// - /// Array of wide characters that contains a null-terminated string that specifies the typeface name of the font. The length of the string must not exceed 32 characters, including the NULL terminator. + /// Gets or sets array of wide characters that contains a null-terminated string that specifies the typeface name of the font. The length of the string must not exceed 32 characters, including the NULL terminator. /// + public string LfFaceName + { + get { return _lfFaceName; } + set { _lfFaceName = value; } + } + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] - public string LfFaceName; + private string _lfFaceName; } } diff --git a/src/modules/previewpane/common/cominterop/MSG.cs b/src/modules/previewpane/common/cominterop/MSG.cs index 58d5fedaa7..c1ec5ccca5 100644 --- a/src/modules/previewpane/common/cominterop/MSG.cs +++ b/src/modules/previewpane/common/cominterop/MSG.cs @@ -11,41 +11,42 @@ namespace Common.ComInterlop /// Contains message information from a thread's message queue. ///
[StructLayout(LayoutKind.Sequential)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Interop")] public struct MSG { /// - /// A handle to the window whose window procedure receives the message. This member is NULL when the message is a thread message. + /// Gets or sets a handle to the window whose window procedure receives the message. This member is NULL when the message is a thread message. /// - public IntPtr Hwnd; + public IntPtr Hwnd { get; set; } /// - /// The message identifier. Applications can only use the low word; the high word is reserved by the system. + /// Gets or sets the message identifier. Applications can only use the low word; the high word is reserved by the system. /// - public int Message; + public int Message { get; set; } /// - /// Additional information about the message. The exact meaning depends on the value of the message member. + /// Gets or sets additional information about the message. The exact meaning depends on the value of the message member. /// - public IntPtr WParam; + public IntPtr WParam { get; set; } /// - /// Additional information about the message. The exact meaning depends on the value of the message member. + /// Gets or sets additional information about the message. The exact meaning depends on the value of the message member. /// - public IntPtr LParam; + public IntPtr LParam { get; set; } /// - /// The time at which the message was posted. + /// Gets or sets the time at which the message was posted. /// - public int Time; + public int Time { get; set; } /// - /// The x coordinate of cursor position, in screen coordinates, when the message was posted. + /// Gets or sets the x coordinate of cursor position, in screen coordinates, when the message was posted. /// - public int PtX; + public int PtX { get; set; } /// - /// The y coordinate of cursor position, in screen coordinates, when the message was posted. + /// Gets or sets the y coordinate of cursor position, in screen coordinates, when the message was posted. /// - public int PtY; + public int PtY { get; set; } } } diff --git a/src/modules/previewpane/common/cominterop/NativeMethods.cs b/src/modules/previewpane/common/cominterop/NativeMethods.cs new file mode 100644 index 0000000000..2a23adaed3 --- /dev/null +++ b/src/modules/previewpane/common/cominterop/NativeMethods.cs @@ -0,0 +1,37 @@ +// 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.Runtime.InteropServices; + +namespace PreviewHandlerCommon.ComInterop +{ + /// + /// Interop methods + /// + internal class NativeMethods + { + /// + /// Changes the parent window of the specified child window. + /// + /// A handle to the child window. + /// A handle to the new parent window. + /// If the function succeeds, the return value is a handle to the previous parent window and NULL in case of failure. + [DllImport("user32.dll")] + public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); + + /// + /// Retrieves the handle to the window that has the keyboard focus, if the window is attached to the calling thread's message queue. + /// + /// The return value is the handle to the window with the keyboard focus. If the calling thread's message queue does not have an associated window with the keyboard focus, the return value is NULL. + [DllImport("user32.dll", CharSet = CharSet.Auto)] + public static extern IntPtr GetFocus(); + + [DllImport("user32.dll", SetLastError = true)] + public static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll", SetLastError = true)] + public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + } +} diff --git a/src/modules/previewpane/common/cominterop/RECT.cs b/src/modules/previewpane/common/cominterop/RECT.cs index 2d47937c39..25eca98b09 100644 --- a/src/modules/previewpane/common/cominterop/RECT.cs +++ b/src/modules/previewpane/common/cominterop/RECT.cs @@ -11,27 +11,28 @@ namespace Common.ComInterlop /// The RECT structure defines a rectangle by the coordinates of its upper-left and lower-right corners. ///
[StructLayout(LayoutKind.Sequential)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Interop")] public struct RECT { /// - /// Specifies the x-coordinate of the upper-left corner of the rectangle. + /// Gets or sets specifies the x-coordinate of the upper-left corner of the rectangle. /// - public int Left; + public int Left { get; set; } /// - /// Specifies the y-coordinate of the upper-left corner of the rectangle. + /// Gets or sets specifies the y-coordinate of the upper-left corner of the rectangle. /// - public int Top; + public int Top { get; set; } /// - /// Specifies the x-coordinate of the lower-right corner of the rectangle. + /// Gets or sets specifies the x-coordinate of the lower-right corner of the rectangle. /// - public int Right; + public int Right { get; set; } /// - /// Specifies the y-coordinate of the lower-right corner of the rectangle. + /// Gets or sets specifies the y-coordinate of the lower-right corner of the rectangle. /// - public int Bottom; + public int Bottom { get; set; } /// /// Creates a structure with the edge locations specified in the struct. @@ -39,7 +40,7 @@ namespace Common.ComInterlop /// Return a . public Rectangle ToRectangle() { - return Rectangle.FromLTRB(this.Left, this.Top, this.Right, this.Bottom); + return Rectangle.FromLTRB(Left, Top, Right, Bottom); } } } diff --git a/src/modules/previewpane/common/controls/FormHandlerControl.cs b/src/modules/previewpane/common/controls/FormHandlerControl.cs index 901b42dc3f..b3696044fc 100644 --- a/src/modules/previewpane/common/controls/FormHandlerControl.cs +++ b/src/modules/previewpane/common/controls/FormHandlerControl.cs @@ -6,6 +6,7 @@ using System; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; +using PreviewHandlerCommon.ComInterop; namespace Common { @@ -34,12 +35,13 @@ namespace Common // This is important, because the thread that instantiates the preview handler component and calls its constructor is a single-threaded apartment (STA) thread, but the thread that calls into the interface members later on is a multithreaded apartment (MTA) thread. Windows Forms controls are meant to run on STA threads. // More details: https://docs.microsoft.com/en-us/archive/msdn-magazine/2007/january/windows-vista-and-office-writing-your-own-preview-handlers. var forceCreation = this.Handle; + this.FormBorderStyle = FormBorderStyle.None; this.Visible = false; } /// - public IntPtr GetHandle() + public IntPtr GetWindowHandle() { return this.Handle; } @@ -50,7 +52,7 @@ namespace Common var getResult = IntPtr.Zero; this.InvokeOnControlThread(() => { - getResult = GetFocus(); + getResult = NativeMethods.GetFocus(); }); result = getResult; } @@ -140,28 +142,6 @@ namespace Common this.Invoke(func); } - /// - /// Changes the parent window of the specified child window. - /// - /// A handle to the child window. - /// A handle to the new parent window. - /// If the function succeeds, the return value is a handle to the previous parent window and NULL in case of failure. - [DllImport("user32.dll")] - private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); - - /// - /// Retrieves the handle to the window that has the keyboard focus, if the window is attached to the calling thread's message queue. - /// - /// The return value is the handle to the window with the keyboard focus. If the calling thread's message queue does not have an associated window with the keyboard focus, the return value is NULL. - [DllImport("user32.dll", CharSet = CharSet.Auto)] - private static extern IntPtr GetFocus(); - - [DllImport("user32.dll", SetLastError = true)] - private static extern int GetWindowLong(IntPtr hWnd, int nIndex); - - [DllImport("user32.dll", SetLastError = true)] - private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); - /// /// Update the Form Control window with the passed rectangle. /// @@ -171,14 +151,14 @@ namespace Common this.InvokeOnControlThread(() => { // We must set the WS_CHILD style to change the form to a control within the Explorer preview pane - int windowStyle = GetWindowLong(this.Handle, gwlStyle); + int windowStyle = NativeMethods.GetWindowLong(Handle, gwlStyle); if ((windowStyle & wsChild) == 0) { - SetWindowLong(this.Handle, gwlStyle, windowStyle | wsChild); + _ = NativeMethods.SetWindowLong(Handle, gwlStyle, windowStyle | wsChild); } - SetParent(this.Handle, this.parentHwnd); - this.Bounds = windowBounds; + NativeMethods.SetParent(Handle, parentHwnd); + Bounds = windowBounds; }); } } diff --git a/src/modules/previewpane/common/controls/IPreviewHandlerControl.cs b/src/modules/previewpane/common/controls/IPreviewHandlerControl.cs index 20271da6da..eb91c28207 100644 --- a/src/modules/previewpane/common/controls/IPreviewHandlerControl.cs +++ b/src/modules/previewpane/common/controls/IPreviewHandlerControl.cs @@ -47,7 +47,7 @@ namespace Common /// Gets the HWND of the control window. /// /// Pointer to the window handle. - IntPtr GetHandle(); + IntPtr GetWindowHandle(); /// /// Hide the preview and free any resource used for the preview. diff --git a/src/modules/previewpane/common/controls/WebBrowserDownloadControlFlags.cs b/src/modules/previewpane/common/controls/WebBrowserDownloadControlFlags.cs index 6023950f8f..bf4743a0e9 100644 --- a/src/modules/previewpane/common/controls/WebBrowserDownloadControlFlags.cs +++ b/src/modules/previewpane/common/controls/WebBrowserDownloadControlFlags.cs @@ -9,6 +9,7 @@ using System; /// Values of flags are defined in mshtmdid.h in distributed Windows Sdk. /// [Flags] +[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Interop, keeping stuff in sync")] public enum WebBrowserDownloadControlFlags : int { /// diff --git a/src/modules/previewpane/common/controls/WebBrowserExt.cs b/src/modules/previewpane/common/controls/WebBrowserExt.cs index c62eb6e389..5dcd44772a 100644 --- a/src/modules/previewpane/common/controls/WebBrowserExt.cs +++ b/src/modules/previewpane/common/controls/WebBrowserExt.cs @@ -51,7 +51,8 @@ namespace PreviewHandlerCommon public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) { object result; - if (name.Equals(DISPIDAMBIENTDLCONTROL)) + + if (name != null && name.Equals(DISPIDAMBIENTDLCONTROL, StringComparison.CurrentCulture)) { result = Convert.ToInt32( WebBrowserDownloadControlFlags.DLIMAGES | @@ -65,11 +66,11 @@ namespace PreviewHandlerCommon WebBrowserDownloadControlFlags.NO_DLACTIVEXCTLS | WebBrowserDownloadControlFlags.NO_RUNACTIVEXCTLS | WebBrowserDownloadControlFlags.NO_BEHAVIORS | - WebBrowserDownloadControlFlags.SILENT); + WebBrowserDownloadControlFlags.SILENT, CultureInfo.CurrentCulture); } else { - result = this.GetType().InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters); + result = GetType().InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters); } return result; diff --git a/src/modules/previewpane/common/examplehandler/CustomControlTest.cs b/src/modules/previewpane/common/examplehandler/CustomControlTest.cs index d9e6e68c25..52fc860e54 100644 --- a/src/modules/previewpane/common/examplehandler/CustomControlTest.cs +++ b/src/modules/previewpane/common/examplehandler/CustomControlTest.cs @@ -2,6 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.IO; using System.Windows.Forms; @@ -24,7 +25,7 @@ namespace Common WebBrowser browser = new WebBrowser(); browser.DocumentText = "Test"; - browser.Navigate(filePath); + browser.Navigate(new Uri(filePath)); browser.Dock = DockStyle.Fill; browser.IsWebBrowserContextMenuEnabled = false; this.Controls.Add(browser); diff --git a/src/modules/previewpane/common/examplehandler/TestCustomHandler.cs b/src/modules/previewpane/common/examplehandler/TestCustomHandler.cs index f5d783020d..2e6828daae 100644 --- a/src/modules/previewpane/common/examplehandler/TestCustomHandler.cs +++ b/src/modules/previewpane/common/examplehandler/TestCustomHandler.cs @@ -2,6 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Runtime.InteropServices; namespace Common @@ -12,21 +13,50 @@ namespace Common [Guid("22a1a8e8-e929-4732-90ce-91eaff38b614")] [ClassInterface(ClassInterfaceType.None)] [ComVisible(true)] - public class TestCustomHandler : FileBasedPreviewHandler + public class TestCustomHandler : FileBasedPreviewHandler, IDisposable { - private CustomControlTest previewHandlerControl; + private CustomControlTest _previewHandlerControl; + private bool disposedValue; /// public override void DoPreview() { - this.previewHandlerControl.DoPreview(this.FilePath); + _previewHandlerControl.DoPreview(FilePath); } /// protected override IPreviewHandlerControl CreatePreviewHandlerControl() { - this.previewHandlerControl = new CustomControlTest(); - return this.previewHandlerControl; + _previewHandlerControl = new CustomControlTest(); + + return _previewHandlerControl; + } + + /// + /// Disposes objects + /// + /// Is Disposing + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + _previewHandlerControl.Dispose(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + disposedValue = true; + } + } + + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); } } } diff --git a/src/modules/previewpane/common/handlers/PreviewHandlerBase.cs b/src/modules/previewpane/common/handlers/PreviewHandlerBase.cs index 6d47cc348d..3eaff72502 100644 --- a/src/modules/previewpane/common/handlers/PreviewHandlerBase.cs +++ b/src/modules/previewpane/common/handlers/PreviewHandlerBase.cs @@ -46,7 +46,14 @@ namespace Common /// public PreviewHandlerBase() { - this.previewControl = this.CreatePreviewHandlerControl(); + } + + /// + /// Initializes a new instance of the class. + /// + public void Initialize() + { + previewControl = CreatePreviewHandlerControl(); } /// @@ -107,7 +114,7 @@ namespace Common /// public void GetWindow(out IntPtr phwnd) { - phwnd = this.previewControl.GetHandle(); + phwnd = this.previewControl.GetWindowHandle(); } /// From 5a07579b7300e71dc24736ae5732e66046742a6b Mon Sep 17 00:00:00 2001 From: Alekhya Date: Wed, 23 Sep 2020 13:20:32 -0700 Subject: [PATCH 12/22] Fix for settings crash on toggling enable button for any PowerToy (#6620) * Removed getSettings from moduleEnabled so that it doesn't have to reopen the file everytime * To Lower to while checking the theme * color picker doesn't reopen the file * use the generalView model config directly for FZ * Made the same change for all the viewmodels so that they access the general view model value directly instead of opening and reading the value from a file each time * removed unused variable * Fix initialization in tests * should read the file only if general settings does not exist * Added interfaces for all the powertoys to use the generalSettings singleton class * Runner is responding to changes in settings, only issue is that every time the general settings page is loaded the information is being read because isInitialized property could not be ignored during serialization * All tests pass * Settings and runner are working as expected with the settings cache * added a null check to read from the file only when the settings process is started * use converter to deserialize an interface * Renamed generalSettings within the cache to be called CommonSettingsConfig * All tests pass, had to initialize the common settings config instance * Added few comments to newly created classes * encapsulating load and store of general view model * reading from file is encapsulated * settings and runner wotk with generic singleton * Shortcut guide works as expected * Fancyzones, shortcutguide and power preview use the settings repository and work as expected * referencing GeneralSettings instead of the settingsRepository within viewmodels * unified access to General settings and removed the IGeneralSettingsData interface * Passing settings to viewmodel as a parameter * removed ISettingsConfig interface from the viewmodels which are not using the singleton to access settings * have to use ISettingsConfig to use GetSettings * refactored tests, all tests pass * Added test for settingsRepository that a single instance is created * Added comments and removed unnecessary headers/code * added settings repository tests * moq for each settings file * Img resizer tests pass * General tests pass * FancyZones tests pass * PowerPreview tests pass * PowerRename tests pass * shortcut guide tests pass * Added GetModuleName to ISettingsConfig * unify the way the Modulename is accessed. It was redeclared in multiple places and this would cause an issue if the name is changed only in one place. All the module names are accessed using the Settings.ModuleName, eg: ShortcutGuideSettings.ModuleName. * create PTRun settings file if it does not exist * GetFile is now a private function. Modified the logic of KBM default.json access and PT Run so that we can re-use GetSettings instead of GetFile. * Added UpgradeSettingsConfiguration to the ISettingsConfig interface so that the settings file can be upgraded based on some condition. Presently, only the GeneralSettings file is utilizing this to change the PT Version number based on the old PT version and the current PT version that it receives from the helper function. Verified that if the PT version is lower in the general settings.json file, settings saves the file with the new version info. * The naming for the PowerToys was inconsistent and the variables were redeclared in multiple places. To have the settings.ModuleName as the main name, all other places should refer to that name. In the tests file the module name for ImgResizer was 'ImageResizer' and not 'Image Resizer'. * renamed lock * Remove unnecessary GetSettingsFileNAme function. It is no longer in use because the code does not use types to create a new BasePTModule object --- .../ColorPickerSettings.cs | 16 ++++- .../FancyZonesSettings.cs | 20 +++++- .../GeneralSettings.cs | 32 ++++++++- .../ISettingsUtils.cs | 5 +- .../ImageResizerSettings.cs | 14 +++- .../Interface/ISettingsConfig.cs | 16 +++++ .../Interface/ISettingsRepository`1.cs | 11 +++ .../KeyboardManagerProfile.cs | 20 +++++- .../KeyboardManagerSettings.cs | 19 ++++-- .../KeysDataModel.cs | 6 ++ .../PowerLauncherSettings.cs | 16 ++++- .../PowerPreviewSettings.cs | 15 ++-- .../PowerRenameLocalProperties.cs | 16 ++++- .../PowerRenameSettings.cs | 14 +++- .../RemapKeysDataModel.cs | 6 ++ .../SettingsRepository`1.cs | 66 ++++++++++++++++++ .../SettingsUtils.cs | 31 ++++++++- .../ShortcutGuideSettings.cs | 14 +++- .../ShortcutsKeyDataModel.cs | 6 ++ .../ViewModels/ColorPickerViewModel.cs | 25 ++++--- .../ViewModels/FancyZonesViewModel.cs | 42 ++++-------- .../ViewModels/GeneralViewModel.cs | 61 ++++++----------- .../ViewModels/ImageResizerViewModel.cs | 32 ++++----- .../ViewModels/KeyboardManagerViewModel.cs | 46 +++++++------ .../ViewModels/PowerLauncherViewModel.cs | 28 ++++---- .../ViewModels/PowerPreviewViewModel.cs | 21 ++---- .../ViewModels/PowerRenameViewModel.cs | 30 +++----- .../ViewModels/ShortcutGuideViewModel.cs | 44 ++++-------- .../Mocks/ISettingsUtilsMocks.cs | 15 ++-- .../ModelsTests/BasePTSettingsTest.cs | 13 +++- .../ModelsTests/SettingsRepositoryTest.cs | 68 +++++++++++++++++++ .../ViewModelTests/ColorPicker.cs | 8 +-- .../ViewModelTests/FancyZones.cs | 47 ++++++++----- .../ViewModelTests/General.cs | 14 ++++ .../ViewModelTests/ImageResizer.cs | 50 ++++++++------ .../ViewModelTests/KeyboardManager.cs | 2 +- .../ViewModelTests/PowerPreview.cs | 14 +++- .../ViewModelTests/PowerRename.cs | 33 ++++++--- .../ViewModelTests/ShortcutGuide.cs | 19 ++++-- .../ModuleEnabledToForegroundConverter.cs | 6 +- .../Views/ColorPickerPage.xaml.cs | 2 +- .../Views/FancyZonesPage.xaml.cs | 2 +- .../Views/GeneralPage.xaml.cs | 1 + .../Views/ImageResizerPage.xaml.cs | 3 +- .../Views/KeyboardManagerPage.xaml.cs | 2 +- .../Views/PowerLauncherPage.xaml.cs | 2 +- .../Views/PowerPreviewPage.xaml.cs | 2 +- .../Views/PowerRenamePage.xaml.cs | 2 +- .../Views/ShellPage.xaml.cs | 2 + .../Views/ShortcutGuidePage.xaml.cs | 3 +- 50 files changed, 668 insertions(+), 314 deletions(-) create mode 100644 src/core/Microsoft.PowerToys.Settings.UI.Lib/Interface/ISettingsConfig.cs create mode 100644 src/core/Microsoft.PowerToys.Settings.UI.Lib/Interface/ISettingsRepository`1.cs create mode 100644 src/core/Microsoft.PowerToys.Settings.UI.Lib/SettingsRepository`1.cs create mode 100644 src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/SettingsRepositoryTest.cs diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ColorPickerSettings.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ColorPickerSettings.cs index 4e1a2be279..849bae1909 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ColorPickerSettings.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ColorPickerSettings.cs @@ -2,14 +2,13 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Text.Json; using System.Text.Json.Serialization; -using Microsoft.PowerToys.Settings.UI.Lib.Utilities; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib { - public class ColorPickerSettings : BasePTModuleSettings + public class ColorPickerSettings : BasePTModuleSettings, ISettingsConfig { public const string ModuleName = "ColorPicker"; @@ -33,5 +32,16 @@ namespace Microsoft.PowerToys.Settings.UI.Lib settingsUtils.SaveSettings(JsonSerializer.Serialize(this, options), ModuleName); } + + public string GetModuleName() + { + return Name; + } + + // This can be utilized in the future if the settings.json file is to be modified/deleted. + public bool UpgradeSettingsConfiguration() + { + return false; + } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/FancyZonesSettings.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/FancyZonesSettings.cs index e3e49aa9f9..570b75cc97 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/FancyZonesSettings.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/FancyZonesSettings.cs @@ -3,19 +3,33 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib { - public class FancyZonesSettings : BasePTModuleSettings + public class FancyZonesSettings : BasePTModuleSettings, ISettingsConfig { + public const string ModuleName = "FancyZones"; + public FancyZonesSettings() { - Version = string.Empty; - Name = string.Empty; + Version = "1.0"; + Name = ModuleName; Properties = new FZConfigProperties(); } [JsonPropertyName("properties")] public FZConfigProperties Properties { get; set; } + + public string GetModuleName() + { + return Name; + } + + // This can be utilized in the future if the settings.json file is to be modified/deleted. + public bool UpgradeSettingsConfiguration() + { + return false; + } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/GeneralSettings.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/GeneralSettings.cs index 6b8a4932b0..637e531516 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/GeneralSettings.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/GeneralSettings.cs @@ -2,12 +2,15 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Text.Json; using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; +using Microsoft.PowerToys.Settings.UI.Lib.Utilities; namespace Microsoft.PowerToys.Settings.UI.Lib { - public class GeneralSettings + public class GeneralSettings : ISettingsConfig { // Gets or sets a value indicating whether packaged. [JsonPropertyName("packaged")] @@ -82,5 +85,32 @@ namespace Microsoft.PowerToys.Settings.UI.Lib { return interop.CommonManaged.GetProductVersion(); } + + // This function is to implement the ISettingsConfig interface. + // This interface helps in getting the settings configurations. + public string GetModuleName() + { + // The SettingsUtils functions access general settings when the module name is an empty string. + return string.Empty; + } + + public bool UpgradeSettingsConfiguration() + { + try + { + if (Helper.CompareVersions(PowertoysVersion, Helper.GetProductVersion()) < 0) + { + // Update settings + PowertoysVersion = Helper.GetProductVersion(); + return true; + } + } + catch (FormatException) + { + // If there is an issue with the version number format, don't migrate settings. + } + + return false; + } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ISettingsUtils.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ISettingsUtils.cs index 3430b0b18c..c5083f3a16 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ISettingsUtils.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ISettingsUtils.cs @@ -2,11 +2,14 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Lib.Interface; + namespace Microsoft.PowerToys.Settings.UI.Lib { public interface ISettingsUtils { - T GetSettings(string powertoy = "", string fileName = "settings.json"); + T GetSettings(string powertoy = "", string fileName = "settings.json") + where T : ISettingsConfig, new(); void SaveSettings(string jsonSettings, string powertoy = "", string fileName = "settings.json"); diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ImageResizerSettings.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ImageResizerSettings.cs index fcf512f591..103d98f655 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ImageResizerSettings.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ImageResizerSettings.cs @@ -4,10 +4,11 @@ using System.Text.Json; using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib { - public class ImageResizerSettings : BasePTModuleSettings + public class ImageResizerSettings : BasePTModuleSettings, ISettingsConfig { public const string ModuleName = "Image Resizer"; @@ -29,5 +30,16 @@ namespace Microsoft.PowerToys.Settings.UI.Lib }; return JsonSerializer.Serialize(this, options); } + + public string GetModuleName() + { + return Name; + } + + // This can be utilized in the future if the settings.json file is to be modified/deleted. + public bool UpgradeSettingsConfiguration() + { + return false; + } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/Interface/ISettingsConfig.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/Interface/ISettingsConfig.cs new file mode 100644 index 0000000000..fa0291568f --- /dev/null +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/Interface/ISettingsConfig.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.PowerToys.Settings.UI.Lib.Interface +{ + // Common interface to be implemented by all the objects which get and store settings properties. + public interface ISettingsConfig + { + string ToJsonString(); + + string GetModuleName(); + + bool UpgradeSettingsConfiguration(); + } +} diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/Interface/ISettingsRepository`1.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/Interface/ISettingsRepository`1.cs new file mode 100644 index 0000000000..6caadb85f9 --- /dev/null +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/Interface/ISettingsRepository`1.cs @@ -0,0 +1,11 @@ +// 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.PowerToys.Settings.UI.Lib.Interface +{ + public interface ISettingsRepository + { + T SettingsConfig { get; set; } + } +} diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/KeyboardManagerProfile.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/KeyboardManagerProfile.cs index 52a0c2d764..66246e2cc4 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/KeyboardManagerProfile.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/KeyboardManagerProfile.cs @@ -2,11 +2,13 @@ // 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.Text.Json; using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib { - public class KeyboardManagerProfile + public class KeyboardManagerProfile : ISettingsConfig { [JsonPropertyName("remapKeys")] public RemapKeysDataModel RemapKeys { get; set; } @@ -19,5 +21,21 @@ namespace Microsoft.PowerToys.Settings.UI.Lib RemapKeys = new RemapKeysDataModel(); RemapShortcuts = new ShortcutsKeyDataModel(); } + + public string ToJsonString() + { + return JsonSerializer.Serialize(this); + } + + public string GetModuleName() + { + return KeyboardManagerSettings.ModuleName; + } + + // This can be utilized in the future if the default.json file is to be modified/deleted. + public bool UpgradeSettingsConfiguration() + { + return false; + } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/KeyboardManagerSettings.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/KeyboardManagerSettings.cs index 12ff998159..ffe032a47e 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/KeyboardManagerSettings.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/KeyboardManagerSettings.cs @@ -3,11 +3,14 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib { - public class KeyboardManagerSettings : BasePTModuleSettings + public class KeyboardManagerSettings : BasePTModuleSettings, ISettingsConfig { + public const string ModuleName = "Keyboard Manager"; + [JsonPropertyName("properties")] public KeyboardManagerProperties Properties { get; set; } @@ -15,14 +18,18 @@ namespace Microsoft.PowerToys.Settings.UI.Lib { Properties = new KeyboardManagerProperties(); Version = "1"; - Name = "_unset_"; + Name = ModuleName; } - public KeyboardManagerSettings(string ptName) + public string GetModuleName() { - Properties = new KeyboardManagerProperties(); - Version = "1"; - Name = ptName; + return Name; + } + + // This can be utilized in the future if the settings.json file is to be modified/deleted. + public bool UpgradeSettingsConfiguration() + { + return false; } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/KeysDataModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/KeysDataModel.cs index 286e5510ae..47f211b26d 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/KeysDataModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/KeysDataModel.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; +using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.PowerToys.Settings.UI.Lib.Utilities; @@ -35,5 +36,10 @@ namespace Microsoft.PowerToys.Settings.UI.Lib { return MapKeys(NewRemapKeys); } + + public string ToJsonString() + { + return JsonSerializer.Serialize(this); + } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherSettings.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherSettings.cs index 7eddad108f..124657664c 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherSettings.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerLauncherSettings.cs @@ -2,14 +2,13 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Text.Json; using System.Text.Json.Serialization; -using Microsoft.PowerToys.Settings.UI.Lib.Utilities; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib { - public class PowerLauncherSettings : BasePTModuleSettings + public class PowerLauncherSettings : BasePTModuleSettings, ISettingsConfig { public const string ModuleName = "PowerToys Run"; @@ -33,5 +32,16 @@ namespace Microsoft.PowerToys.Settings.UI.Lib settingsUtils.SaveSettings(JsonSerializer.Serialize(this, options), ModuleName); } + + public string GetModuleName() + { + return Name; + } + + // This can be utilized in the future if the settings.json file is to be modified/deleted. + public bool UpgradeSettingsConfiguration() + { + return false; + } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerPreviewSettings.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerPreviewSettings.cs index afe67754f5..e56be7273d 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerPreviewSettings.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerPreviewSettings.cs @@ -3,10 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib { - public class PowerPreviewSettings : BasePTModuleSettings + public class PowerPreviewSettings : BasePTModuleSettings, ISettingsConfig { public const string ModuleName = "File Explorer"; @@ -20,11 +21,15 @@ namespace Microsoft.PowerToys.Settings.UI.Lib Name = ModuleName; } - public PowerPreviewSettings(string ptName) + public string GetModuleName() { - Properties = new PowerPreviewProperties(); - Version = "1"; - Name = ptName; + return Name; + } + + // This can be utilized in the future if the settings.json file is to be modified/deleted. + public bool UpgradeSettingsConfiguration() + { + return false; } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerRenameLocalProperties.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerRenameLocalProperties.cs index a0ba1966fe..3136edd2a6 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerRenameLocalProperties.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerRenameLocalProperties.cs @@ -3,10 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Text.Json; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib { - public class PowerRenameLocalProperties + public class PowerRenameLocalProperties : ISettingsConfig { public PowerRenameLocalProperties() { @@ -51,5 +52,18 @@ namespace Microsoft.PowerToys.Settings.UI.Lib { return JsonSerializer.Serialize(this); } + + // This function is required to implement the ISettingsConfig interface and obtain the settings configurations. + public string GetModuleName() + { + string moduleName = PowerRenameSettings.ModuleName; + return moduleName; + } + + // This can be utilized in the future if the settings.json file is to be modified/deleted. + public bool UpgradeSettingsConfiguration() + { + return false; + } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerRenameSettings.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerRenameSettings.cs index a3be125bc5..0d7dc58a98 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerRenameSettings.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerRenameSettings.cs @@ -3,10 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib { - public class PowerRenameSettings : BasePTModuleSettings + public class PowerRenameSettings : BasePTModuleSettings, ISettingsConfig { public const string ModuleName = "PowerRename"; @@ -39,5 +40,16 @@ namespace Microsoft.PowerToys.Settings.UI.Lib Version = "1"; Name = ptName; } + + public string GetModuleName() + { + return Name; + } + + // This can be utilized in the future if the power-rename-settings.json file is to be modified/deleted. + public bool UpgradeSettingsConfiguration() + { + return false; + } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/RemapKeysDataModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/RemapKeysDataModel.cs index 4f359197ae..873d5d7d64 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/RemapKeysDataModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/RemapKeysDataModel.cs @@ -3,6 +3,7 @@ // 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 Microsoft.PowerToys.Settings.UI.Lib @@ -16,5 +17,10 @@ namespace Microsoft.PowerToys.Settings.UI.Lib { InProcessRemapKeys = new List(); } + + public string ToJsonString() + { + return JsonSerializer.Serialize(this); + } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/SettingsRepository`1.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/SettingsRepository`1.cs new file mode 100644 index 0000000000..3bb7df8518 --- /dev/null +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/SettingsRepository`1.cs @@ -0,0 +1,66 @@ +// 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 Microsoft.PowerToys.Settings.UI.Lib.Interface; + +namespace Microsoft.PowerToys.Settings.UI.Lib +{ + // This Singleton class is a wrapper around the settings configurations that are accessed by viewmodels. + // This class can have only one instance and therefore the settings configurations are common to all. + public class SettingsRepository : ISettingsRepository + where T : class, ISettingsConfig, new() + { + private static readonly object _SettingsRepoLock = new object(); + + private static ISettingsUtils _settingsUtils; + + private static SettingsRepository settingsRepository; + + private T settingsConfig; + + public static SettingsRepository GetInstance(ISettingsUtils settingsUtils) + { + // To ensure that only one instance of Settings Repository is created in a multi-threaded environment. + lock (_SettingsRepoLock) + { + if (settingsRepository == null) + { + settingsRepository = new SettingsRepository(); + _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); + } + + return settingsRepository; + } + } + + // The Singleton class must have a private constructor so that it cannot be instantiated by any other object other than itself. + private SettingsRepository() + { + } + + // Settings configurations shared across all viewmodels + public T SettingsConfig + { + get + { + if (settingsConfig == null) + { + T settingsItem = new T(); + settingsConfig = _settingsUtils.GetSettings(settingsItem.GetModuleName()); + } + + return settingsConfig; + } + + set + { + if (value != null) + { + settingsConfig = value; + } + } + } + } +} diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/SettingsUtils.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/SettingsUtils.cs index b0096bbb9c..c939f71536 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/SettingsUtils.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/SettingsUtils.cs @@ -4,6 +4,7 @@ using System; using System.Text.Json; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; using Microsoft.PowerToys.Settings.UI.Lib.Utilities; namespace Microsoft.PowerToys.Settings.UI.Lib @@ -59,16 +60,42 @@ namespace Microsoft.PowerToys.Settings.UI.Lib /// /// Get a Deserialized object of the json settings string. + /// This function creates a file in the powertoy folder if it does not exist and returns an object with default properties. /// /// Deserialized json settings object. public T GetSettings(string powertoy = DefaultModuleName, string fileName = DefaultFileName) + where T : ISettingsConfig, new() + { + if (SettingsExists(powertoy, fileName)) + { + // Given the file already exists, to deserialize the file and read it's content. + T deserializedSettings = GetFile(powertoy, fileName); + + // IF the file needs to be modified, to save the new configurations accordingly. + if (!deserializedSettings.UpgradeSettingsConfiguration()) + { + SaveSettings(deserializedSettings.ToJsonString(), powertoy, fileName); + } + + return deserializedSettings; + } + else + { + // If the settings file does not exist, to create a new object with default parameters and save it to a newly created settings file. + T newSettingsItem = new T(); + SaveSettings(newSettingsItem.ToJsonString(), powertoy, fileName); + return newSettingsItem; + } + } + + // Given the powerToy folder name and filename to be accessed, this function deserializes and returns the file. + private T GetFile(string powertoyFolderName = DefaultModuleName, string fileName = DefaultFileName) { // Adding Trim('\0') to overcome possible NTFS file corruption. // Look at issue https://github.com/microsoft/PowerToys/issues/6413 you'll see the file has a large sum of \0 to fill up a 4096 byte buffer for writing to disk // This, while not totally ideal, does work around the problem by trimming the end. // The file itself did write the content correctly but something is off with the actual end of the file, hence the 0x00 bug - var jsonSettingsString = _ioProvider.ReadAllText(GetSettingsPath(powertoy, fileName)).Trim('\0'); - + var jsonSettingsString = _ioProvider.ReadAllText(GetSettingsPath(powertoyFolderName, fileName)).Trim('\0'); return JsonSerializer.Deserialize(jsonSettingsString); } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ShortcutGuideSettings.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ShortcutGuideSettings.cs index 6b5e41e631..343649fd80 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ShortcutGuideSettings.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ShortcutGuideSettings.cs @@ -3,10 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib { - public class ShortcutGuideSettings : BasePTModuleSettings + public class ShortcutGuideSettings : BasePTModuleSettings, ISettingsConfig { public const string ModuleName = "Shortcut Guide"; @@ -19,5 +20,16 @@ namespace Microsoft.PowerToys.Settings.UI.Lib Properties = new ShortcutGuideProperties(); Version = "1.0"; } + + public string GetModuleName() + { + return Name; + } + + // This can be utilized in the future if the settings.json file is to be modified/deleted. + public bool UpgradeSettingsConfiguration() + { + return false; + } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ShortcutsKeyDataModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ShortcutsKeyDataModel.cs index 0e1e131fe3..5195c6c1e9 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ShortcutsKeyDataModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ShortcutsKeyDataModel.cs @@ -3,6 +3,7 @@ // 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 Microsoft.PowerToys.Settings.UI.Lib @@ -20,5 +21,10 @@ namespace Microsoft.PowerToys.Settings.UI.Lib GlobalRemapShortcuts = new List(); AppSpecificRemapShortcuts = new List(); } + + public string ToJsonString() + { + return JsonSerializer.Serialize(this); + } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ColorPickerViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ColorPickerViewModel.cs index 6d49d42355..5df080b77d 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ColorPickerViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ColorPickerViewModel.cs @@ -5,20 +5,27 @@ using System; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; -using Microsoft.PowerToys.Settings.UI.Lib.Utilities; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class ColorPickerViewModel : Observable { + private GeneralSettings GeneralSettingsConfig { get; set; } + private readonly ISettingsUtils _settingsUtils; + private ColorPickerSettings _colorPickerSettings; + private bool _isEnabled; private Func SendConfigMSG { get; } - public ColorPickerViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc) + public ColorPickerViewModel(ISettingsUtils settingsUtils, ISettingsRepository settingsRepository, Func ipcMSGCallBackFunc) { + // Obtain the general PowerToy settings configurations + GeneralSettingsConfig = settingsRepository.SettingsConfig; + _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); if (_settingsUtils.SettingsExists(ColorPickerSettings.ModuleName)) { @@ -29,11 +36,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels _colorPickerSettings = new ColorPickerSettings(); } - if (_settingsUtils.SettingsExists()) - { - var generalSettings = _settingsUtils.GetSettings(); - _isEnabled = generalSettings.Enabled.ColorPicker; - } + _isEnabled = GeneralSettingsConfig.Enabled.ColorPicker; // set the callback functions value to hangle outgoing IPC message. SendConfigMSG = ipcMSGCallBackFunc; @@ -53,10 +56,10 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels _isEnabled = value; OnPropertyChanged(nameof(IsEnabled)); - // grab the latest version of settings - var generalSettings = _settingsUtils.GetSettings(); - generalSettings.Enabled.ColorPicker = value; - OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(generalSettings); + // Set the status of ColorPicker in the general settings + GeneralSettingsConfig.Enabled.ColorPicker = value; + OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig); + SendConfigMSG(outgoing.ToString()); } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/FancyZonesViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/FancyZonesViewModel.cs index 46929bb2a5..65eb24ad3b 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/FancyZonesViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/FancyZonesViewModel.cs @@ -6,16 +6,16 @@ using System; using System.Drawing; using System.Runtime.CompilerServices; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; -using Microsoft.PowerToys.Settings.UI.Lib.Utilities; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels.Commands; namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class FancyZonesViewModel : Observable { - private readonly ISettingsUtils _settingsUtils; + private GeneralSettings GeneralSettingsConfig { get; set; } - private const string ModuleName = "FancyZones"; + private const string ModuleName = FancyZonesSettings.ModuleName; public ButtonClickCommand LaunchEditorEventHandler { get; set; } @@ -25,20 +25,14 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels private string settingsConfigFileFolder = string.Empty; - public FancyZonesViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc, string configFileSubfolder = "") + public FancyZonesViewModel(ISettingsRepository settingsRepository, ISettingsRepository moduleSettingsRepository, Func ipcMSGCallBackFunc, string configFileSubfolder = "") { + // To obtain the general settings configurations of PowerToys Settings. + GeneralSettingsConfig = settingsRepository.SettingsConfig; settingsConfigFileFolder = configFileSubfolder; - _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); - try - { - Settings = _settingsUtils.GetSettings(GetSettingsSubPath()); - } - catch - { - Settings = new FancyZonesSettings(); - _settingsUtils.SaveSettings(Settings.ToJsonString(), GetSettingsSubPath()); - } + // To obtain the settings configurations of Fancy zones. + Settings = moduleSettingsRepository.SettingsConfig; LaunchEditorEventHandler = new ButtonClickCommand(LaunchEditor); @@ -72,18 +66,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels string highlightColor = Settings.Properties.FancyzonesZoneHighlightColor.Value; _zoneHighlightColor = highlightColor != string.Empty ? highlightColor : "#0078D7"; - GeneralSettings generalSettings; - try - { - generalSettings = _settingsUtils.GetSettings(string.Empty); - } - catch - { - generalSettings = new GeneralSettings(); - _settingsUtils.SaveSettings(generalSettings.ToJsonString(), string.Empty); - } - - _isEnabled = generalSettings.Enabled.FancyZones; + _isEnabled = GeneralSettingsConfig.Enabled.FancyZones; } private bool _isEnabled; @@ -121,9 +104,10 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels if (value != _isEnabled) { _isEnabled = value; - GeneralSettings generalSettings = _settingsUtils.GetSettings(string.Empty); - generalSettings.Enabled.FancyZones = value; - OutGoingGeneralSettings snd = new OutGoingGeneralSettings(generalSettings); + + // Set the status of FancyZones in the general settings configuration + GeneralSettingsConfig.Enabled.FancyZones = value; + OutGoingGeneralSettings snd = new OutGoingGeneralSettings(GeneralSettingsConfig); SendConfigMSG(snd.ToString()); OnPropertyChanged("IsEnabled"); diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/GeneralViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/GeneralViewModel.cs index 19864f120c..380fd28e8e 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/GeneralViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/GeneralViewModel.cs @@ -3,9 +3,9 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics; using System.Runtime.CompilerServices; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; using Microsoft.PowerToys.Settings.UI.Lib.Utilities; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels.Commands; @@ -13,9 +13,9 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class GeneralViewModel : Observable { - private readonly ISettingsUtils _settingsUtils; + private GeneralSettings GeneralSettingsConfig { get; set; } - private GeneralSettings GeneralSettingsConfigs { get; set; } + private readonly ISettingsUtils _settingsUtils; public ButtonClickCommand CheckFoUpdatesEventHandler { get; set; } @@ -35,33 +35,14 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels private string _settingsConfigFileFolder = string.Empty; - public GeneralViewModel(ISettingsUtils settingsUtils, string runAsAdminText, string runAsUserText, bool isElevated, bool isAdmin, Func updateTheme, Func ipcMSGCallBackFunc, Func ipcMSGRestartAsAdminMSGCallBackFunc, Func ipcMSGCheckForUpdatesCallBackFunc, string configFileSubfolder = "") + public GeneralViewModel(ISettingsUtils settingsUtils, ISettingsRepository settingsRepository, string runAsAdminText, string runAsUserText, bool isElevated, bool isAdmin, Func updateTheme, Func ipcMSGCallBackFunc, Func ipcMSGRestartAsAdminMSGCallBackFunc, Func ipcMSGCheckForUpdatesCallBackFunc, string configFileSubfolder = "") { CheckFoUpdatesEventHandler = new ButtonClickCommand(CheckForUpdates_Click); RestartElevatedButtonEventHandler = new ButtonClickCommand(Restart_Elevated); _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); - try - { - GeneralSettingsConfigs = _settingsUtils.GetSettings(string.Empty); - - if (Helper.CompareVersions(GeneralSettingsConfigs.PowertoysVersion, Helper.GetProductVersion()) < 0) - { - // Update settings - GeneralSettingsConfigs.PowertoysVersion = Helper.GetProductVersion(); - _settingsUtils.SaveSettings(GeneralSettingsConfigs.ToJsonString(), string.Empty); - } - } - catch (FormatException e) - { - // If there is an issue with the version number format, don't migrate settings. - Debug.WriteLine(e.Message); - } - catch - { - GeneralSettingsConfigs = new GeneralSettings(); - _settingsUtils.SaveSettings(GeneralSettingsConfigs.ToJsonString(), string.Empty); - } + // To obtain the general settings configuration of PowerToys if it exists, else to create a new file and return the default configurations. + GeneralSettingsConfig = settingsRepository.SettingsConfig; // set the callback functions value to hangle outgoing IPC message. SendConfigMSG = ipcMSGCallBackFunc; @@ -70,12 +51,12 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels // set the callback function value to update the UI theme. UpdateUIThemeCallBack = updateTheme; - UpdateUIThemeCallBack(GeneralSettingsConfigs.Theme.ToLower()); + UpdateUIThemeCallBack(GeneralSettingsConfig.Theme.ToLower()); // Update Settings file folder: _settingsConfigFileFolder = configFileSubfolder; - switch (GeneralSettingsConfigs.Theme.ToLower()) + switch (GeneralSettingsConfig.Theme.ToLower()) { case "light": _isLightThemeRadioButtonChecked = true; @@ -88,10 +69,10 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels break; } - _startup = GeneralSettingsConfigs.Startup; - _autoDownloadUpdates = GeneralSettingsConfigs.AutoDownloadUpdates; + _startup = GeneralSettingsConfig.Startup; + _autoDownloadUpdates = GeneralSettingsConfig.AutoDownloadUpdates; _isElevated = isElevated; - _runElevated = GeneralSettingsConfigs.RunElevated; + _runElevated = GeneralSettingsConfig.RunElevated; RunningAsUserDefaultText = runAsUserText; RunningAsAdminDefaultText = runAsAdminText; @@ -142,7 +123,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels if (_startup != value) { _startup = value; - GeneralSettingsConfigs.Startup = value; + GeneralSettingsConfig.Startup = value; RaisePropertyChanged(); } } @@ -214,7 +195,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels if (_runElevated != value) { _runElevated = value; - GeneralSettingsConfigs.RunElevated = value; + GeneralSettingsConfig.RunElevated = value; RaisePropertyChanged(); } } @@ -241,7 +222,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels if (_autoDownloadUpdates != value) { _autoDownloadUpdates = value; - GeneralSettingsConfigs.AutoDownloadUpdates = value; + GeneralSettingsConfig.AutoDownloadUpdates = value; RaisePropertyChanged(); } } @@ -258,11 +239,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { if (value == true) { - GeneralSettingsConfigs.Theme = "dark"; + GeneralSettingsConfig.Theme = "dark"; _isDarkThemeRadioButtonChecked = value; try { - UpdateUIThemeCallBack(GeneralSettingsConfigs.Theme); + UpdateUIThemeCallBack(GeneralSettingsConfig.Theme); } catch { @@ -284,11 +265,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { if (value == true) { - GeneralSettingsConfigs.Theme = "light"; + GeneralSettingsConfig.Theme = "light"; _isLightThemeRadioButtonChecked = value; try { - UpdateUIThemeCallBack(GeneralSettingsConfigs.Theme); + UpdateUIThemeCallBack(GeneralSettingsConfig.Theme); } catch { @@ -310,11 +291,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { if (value == true) { - GeneralSettingsConfigs.Theme = "system"; + GeneralSettingsConfig.Theme = "system"; _isSystemThemeRadioButtonChecked = value; try { - UpdateUIThemeCallBack(GeneralSettingsConfigs.Theme); + UpdateUIThemeCallBack(GeneralSettingsConfig.Theme); } catch { @@ -355,7 +336,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { // Notify UI of property change OnPropertyChanged(propertyName); - OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(GeneralSettingsConfigs); + OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(GeneralSettingsConfig); SendConfigMSG(outsettings.ToString()); } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ImageResizerViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ImageResizerViewModel.cs index 5110092799..ea12e18629 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ImageResizerViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ImageResizerViewModel.cs @@ -7,24 +7,29 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; -using Microsoft.PowerToys.Settings.UI.Lib.Utilities; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class ImageResizerViewModel : Observable { + private GeneralSettings GeneralSettingsConfig { get; set; } + private readonly ISettingsUtils _settingsUtils; private ImageResizerSettings Settings { get; set; } - private const string ModuleName = "ImageResizer"; + private const string ModuleName = ImageResizerSettings.ModuleName; private Func SendConfigMSG { get; } - public ImageResizerViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc) + public ImageResizerViewModel(ISettingsUtils settingsUtils, ISettingsRepository settingsRepository, Func ipcMSGCallBackFunc) { _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); + // To obtain the general settings configurations of PowerToys. + GeneralSettingsConfig = settingsRepository.SettingsConfig; + try { Settings = _settingsUtils.GetSettings(ModuleName); @@ -35,22 +40,10 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels _settingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); } - GeneralSettings generalSettings; - - try - { - generalSettings = _settingsUtils.GetSettings(string.Empty); - } - catch - { - generalSettings = new GeneralSettings(); - _settingsUtils.SaveSettings(generalSettings.ToJsonString(), string.Empty); - } - // set the callback functions value to hangle outgoing IPC message. SendConfigMSG = ipcMSGCallBackFunc; - _isEnabled = generalSettings.Enabled.ImageResizer; + _isEnabled = GeneralSettingsConfig.Enabled.ImageResizer; _advancedSizes = Settings.Properties.ImageresizerSizes.Value; _jpegQualityLevel = Settings.Properties.ImageresizerJpegQualityLevel.Value; _pngInterlaceOption = Settings.Properties.ImageresizerPngInterlaceOption.Value; @@ -88,10 +81,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { if (value != _isEnabled) { + // To set the status of ImageResizer in the General PowerToys settings. _isEnabled = value; - GeneralSettings generalSettings = _settingsUtils.GetSettings(string.Empty); - generalSettings.Enabled.ImageResizer = value; - OutGoingGeneralSettings snd = new OutGoingGeneralSettings(generalSettings); + GeneralSettingsConfig.Enabled.ImageResizer = value; + OutGoingGeneralSettings snd = new OutGoingGeneralSettings(GeneralSettingsConfig); + SendConfigMSG(snd.ToString()); OnPropertyChanged("IsEnabled"); } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/KeyboardManagerViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/KeyboardManagerViewModel.cs index 7f2df05c49..0e6d36f519 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/KeyboardManagerViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/KeyboardManagerViewModel.cs @@ -4,11 +4,13 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Input; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; using Microsoft.PowerToys.Settings.UI.Lib.Utilities; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels.Commands; @@ -16,9 +18,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class KeyboardManagerViewModel : Observable { + private GeneralSettings GeneralSettingsConfig { get; set; } + private readonly ISettingsUtils _settingsUtils; - private const string PowerToyName = "Keyboard Manager"; + private const string PowerToyName = KeyboardManagerSettings.ModuleName; private const string RemapKeyboardActionName = "RemapKeyboard"; private const string RemapKeyboardActionValue = "Open Remap Keyboard Window"; private const string EditShortcutActionName = "EditShortcut"; @@ -32,14 +36,15 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels private ICommand _remapKeyboardCommand; private ICommand _editShortcutCommand; private KeyboardManagerProfile _profile; - private GeneralSettings _generalSettings; private Func SendConfigMSG { get; } private Func, int> FilterRemapKeysList { get; } - public KeyboardManagerViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc, Func, int> filterRemapKeysList) + public KeyboardManagerViewModel(ISettingsUtils settingsUtils, ISettingsRepository settingsRepository, Func ipcMSGCallBackFunc, Func, int> filterRemapKeysList) { + GeneralSettingsConfig = settingsRepository.SettingsConfig; + // set the callback functions value to hangle outgoing IPC message. SendConfigMSG = ipcMSGCallBackFunc; FilterRemapKeysList = filterRemapKeysList; @@ -59,35 +64,25 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels } else { - Settings = new KeyboardManagerSettings(PowerToyName); + Settings = new KeyboardManagerSettings(); _settingsUtils.SaveSettings(Settings.ToJsonString(), PowerToyName); } - - if (_settingsUtils.SettingsExists()) - { - _generalSettings = _settingsUtils.GetSettings(string.Empty); - } - else - { - _generalSettings = new GeneralSettings(); - _settingsUtils.SaveSettings(_generalSettings.ToJsonString(), string.Empty); - } } public bool Enabled { get { - return _generalSettings.Enabled.KeyboardManager; + return GeneralSettingsConfig.Enabled.KeyboardManager; } set { - if (_generalSettings.Enabled.KeyboardManager != value) + if (GeneralSettingsConfig.Enabled.KeyboardManager != value) { - _generalSettings.Enabled.KeyboardManager = value; + GeneralSettingsConfig.Enabled.KeyboardManager = value; OnPropertyChanged(nameof(Enabled)); - OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(_generalSettings); + OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig); SendConfigMSG(outgoing.ToString()); } @@ -177,8 +172,19 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels // update the UI element here. try { - _profile = _settingsUtils.GetSettings(PowerToyName, Settings.Properties.ActiveConfiguration.Value + JsonFileType); - FilterRemapKeysList(_profile.RemapKeys.InProcessRemapKeys); + string fileName = Settings.Properties.ActiveConfiguration.Value + JsonFileType; + + if (_settingsUtils.SettingsExists(PowerToyName, fileName)) + { + _profile = _settingsUtils.GetSettings(PowerToyName, fileName); + } + else + { + // The KBM process out of runner creates the default.json file if it does not exist. + success = false; + } + + FilterRemapKeysList(_profile?.RemapKeys?.InProcessRemapKeys); } finally { diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerLauncherViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerLauncherViewModel.cs index 73e15ae558..82af7c1b5d 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerLauncherViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerLauncherViewModel.cs @@ -6,16 +6,17 @@ using System; using System.Runtime.CompilerServices; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; -using Microsoft.PowerToys.Settings.UI.Lib.Utilities; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class PowerLauncherViewModel : Observable { + private GeneralSettings GeneralSettingsConfig { get; set; } + private readonly ISettingsUtils _settingsUtils; private PowerLauncherSettings settings; - private GeneralSettings generalSettings; public delegate void SendCallback(PowerLauncherSettings settings); @@ -23,10 +24,13 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels private Func SendConfigMSG { get; } - public PowerLauncherViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc, int defaultKeyCode) + public PowerLauncherViewModel(ISettingsUtils settingsUtils, ISettingsRepository settingsRepository, Func ipcMSGCallBackFunc, int defaultKeyCode) { _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); + // To obtain the general Settings configurations of PowerToys + GeneralSettingsConfig = settingsRepository.SettingsConfig; + // set the callback functions value to hangle outgoing IPC message. SendConfigMSG = ipcMSGCallBackFunc; callback = (PowerLauncherSettings settings) => @@ -35,6 +39,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels SendConfigMSG( string.Format("{{ \"powertoys\": {{ \"{0}\": {1} }} }}", PowerLauncherSettings.ModuleName, JsonSerializer.Serialize(settings))); }; + if (_settingsUtils.SettingsExists(PowerLauncherSettings.ModuleName)) { settings = _settingsUtils.GetSettings(PowerLauncherSettings.ModuleName); @@ -47,15 +52,6 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels settings.Properties.MaximumNumberOfResults = 4; callback(settings); } - - if (_settingsUtils.SettingsExists()) - { - generalSettings = _settingsUtils.GetSettings(); - } - else - { - generalSettings = new GeneralSettings(); - } } public PowerLauncherViewModel(PowerLauncherSettings settings, SendCallback callback) @@ -76,16 +72,16 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { get { - return generalSettings.Enabled.PowerLauncher; + return GeneralSettingsConfig.Enabled.PowerLauncher; } set { - if (generalSettings.Enabled.PowerLauncher != value) + if (GeneralSettingsConfig.Enabled.PowerLauncher != value) { - generalSettings.Enabled.PowerLauncher = value; + GeneralSettingsConfig.Enabled.PowerLauncher = value; OnPropertyChanged(nameof(EnablePowerLauncher)); - OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(generalSettings); + OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig); SendConfigMSG(outgoing.ToString()); } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerPreviewViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerPreviewViewModel.cs index 7415c8eb65..0689bc9ca3 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerPreviewViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerPreviewViewModel.cs @@ -5,15 +5,13 @@ using System; using System.Runtime.CompilerServices; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; -using Microsoft.PowerToys.Settings.UI.Lib.Utilities; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class PowerPreviewViewModel : Observable { - private readonly ISettingsUtils _settingsUtils; - - private const string ModuleName = "File Explorer"; + private const string ModuleName = PowerPreviewSettings.ModuleName; private PowerPreviewSettings Settings { get; set; } @@ -21,21 +19,14 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels private string _settingsConfigFileFolder = string.Empty; - public PowerPreviewViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc, string configFileSubfolder = "") + public PowerPreviewViewModel(ISettingsRepository moduleSettingsRepository, Func ipcMSGCallBackFunc, string configFileSubfolder = "") { // Update Settings file folder: _settingsConfigFileFolder = configFileSubfolder; - _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); - try - { - Settings = _settingsUtils.GetSettings(GetSettingsSubPath()); - } - catch - { - Settings = new PowerPreviewSettings(); - _settingsUtils.SaveSettings(Settings.ToJsonString(), GetSettingsSubPath()); - } + // To obtain the PowerPreview settings if it exists. + // If the file does not exist, to create a new one and return the default settings configurations. + Settings = moduleSettingsRepository.SettingsConfig; // set the callback functions value to hangle outgoing IPC message. SendConfigMSG = ipcMSGCallBackFunc; diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerRenameViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerRenameViewModel.cs index 727dfd676b..2a6aaa2bf3 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerRenameViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerRenameViewModel.cs @@ -5,15 +5,17 @@ using System; using System.Runtime.CompilerServices; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; -using Microsoft.PowerToys.Settings.UI.Lib.Utilities; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class PowerRenameViewModel : Observable { + private GeneralSettings GeneralSettingsConfig { get; set; } + private readonly ISettingsUtils _settingsUtils; - private const string ModuleName = "PowerRename"; + private const string ModuleName = PowerRenameSettings.ModuleName; private string _settingsConfigFileFolder = string.Empty; @@ -21,12 +23,14 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels private Func SendConfigMSG { get; } - public PowerRenameViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc, string configFileSubfolder = "") + public PowerRenameViewModel(ISettingsUtils settingsUtils, ISettingsRepository settingsRepository, Func ipcMSGCallBackFunc, string configFileSubfolder = "") { // Update Settings file folder: _settingsConfigFileFolder = configFileSubfolder; _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); + GeneralSettingsConfig = settingsRepository.SettingsConfig; + try { PowerRenameLocalProperties localSettings = _settingsUtils.GetSettings(GetSettingsSubPath(), "power-rename-settings.json"); @@ -47,19 +51,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels _powerRenameRestoreFlagsOnLaunch = Settings.Properties.PersistState.Value; _powerRenameMaxDispListNumValue = Settings.Properties.MaxMRUSize.Value; _autoComplete = Settings.Properties.MRUEnabled.Value; - - GeneralSettings generalSettings; - try - { - generalSettings = _settingsUtils.GetSettings(string.Empty); - } - catch - { - generalSettings = new GeneralSettings(); - _settingsUtils.SaveSettings(generalSettings.ToJsonString(), string.Empty); - } - - _powerRenameEnabled = generalSettings.Enabled.PowerRename; + _powerRenameEnabled = GeneralSettingsConfig.Enabled.PowerRename; } private bool _powerRenameEnabled = false; @@ -80,9 +72,9 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { if (value != _powerRenameEnabled) { - GeneralSettings generalSettings = _settingsUtils.GetSettings(string.Empty); - generalSettings.Enabled.PowerRename = value; - OutGoingGeneralSettings snd = new OutGoingGeneralSettings(generalSettings); + GeneralSettingsConfig.Enabled.PowerRename = value; + OutGoingGeneralSettings snd = new OutGoingGeneralSettings(GeneralSettingsConfig); + SendConfigMSG(snd.ToString()); _powerRenameEnabled = value; diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ShortcutGuideViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ShortcutGuideViewModel.cs index 6c4f5d645e..94806854af 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ShortcutGuideViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/ShortcutGuideViewModel.cs @@ -5,54 +5,38 @@ using System; using System.Runtime.CompilerServices; using Microsoft.PowerToys.Settings.UI.Lib.Helpers; -using Microsoft.PowerToys.Settings.UI.Lib.Utilities; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels { public class ShortcutGuideViewModel : Observable { - private readonly ISettingsUtils _settingsUtils; + private GeneralSettings GeneralSettingsConfig { get; set; } private ShortcutGuideSettings Settings { get; set; } - private const string ModuleName = "Shortcut Guide"; + private const string ModuleName = ShortcutGuideSettings.ModuleName; private Func SendConfigMSG { get; } private string _settingsConfigFileFolder = string.Empty; - public ShortcutGuideViewModel(ISettingsUtils settingsUtils, Func ipcMSGCallBackFunc, string configFileSubfolder = "") + public ShortcutGuideViewModel(ISettingsRepository settingsRepository, ISettingsRepository moduleSettingsRepository, Func ipcMSGCallBackFunc, string configFileSubfolder = "") { // Update Settings file folder: _settingsConfigFileFolder = configFileSubfolder; - _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); - try - { - Settings = _settingsUtils.GetSettings(GetSettingsSubPath()); - } - catch - { - Settings = new ShortcutGuideSettings(); - _settingsUtils.SaveSettings(Settings.ToJsonString(), GetSettingsSubPath()); - } + // To obtain the general PowerToys settings. + GeneralSettingsConfig = settingsRepository.SettingsConfig; - GeneralSettings generalSettings; - - try - { - generalSettings = _settingsUtils.GetSettings(string.Empty); - } - catch - { - generalSettings = new GeneralSettings(); - _settingsUtils.SaveSettings(generalSettings.ToJsonString(), string.Empty); - } + // To obtain the shortcut guide settings, if the file exists. + // If not, to create a file with the default settings and to return the default configurations. + Settings = moduleSettingsRepository.SettingsConfig; // set the callback functions value to hangle outgoing IPC message. SendConfigMSG = ipcMSGCallBackFunc; - _isEnabled = generalSettings.Enabled.ShortcutGuide; + _isEnabled = GeneralSettingsConfig.Enabled.ShortcutGuide; _pressTime = Settings.Properties.PressTime.Value; _opacity = Settings.Properties.OverlayOpacity.Value; @@ -91,9 +75,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels if (value != _isEnabled) { _isEnabled = value; - GeneralSettings generalSettings = _settingsUtils.GetSettings(string.Empty); - generalSettings.Enabled.ShortcutGuide = value; - OutGoingGeneralSettings snd = new OutGoingGeneralSettings(generalSettings); + + // To update the status of shortcut guide in General PowerToy settings. + GeneralSettingsConfig.Enabled.ShortcutGuide = value; + OutGoingGeneralSettings snd = new OutGoingGeneralSettings(GeneralSettingsConfig); + SendConfigMSG(snd.ToString()); OnPropertyChanged("IsEnabled"); } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/ISettingsUtilsMocks.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/ISettingsUtilsMocks.cs index e1e200ce96..b1c47f15b1 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/ISettingsUtilsMocks.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/ISettingsUtilsMocks.cs @@ -1,24 +1,19 @@ using Microsoft.PowerToys.Settings.UI.Lib; -using Microsoft.PowerToys.Settings.UI.Lib.Utilities; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; using Moq; using System; -using System.Collections.Generic; -using System.Text; namespace Microsoft.PowerToys.Settings.UI.UnitTests.Mocks { internal static class ISettingsUtilsMocks { //Stubs out empty values for imageresizersettings and general settings as needed by the imageresizer viewmodel - internal static Mock GetStubSettingsUtils() + internal static Mock GetStubSettingsUtils() + where T : ISettingsConfig, new() { var settingsUtils = new Mock(); - settingsUtils.Setup(x => x.GetSettings(It.IsAny(), It.IsAny())) - .Returns(new InvocationFunc(invocation => - { - var typeArgument = invocation.Method.GetGenericArguments()[0]; - return Activator.CreateInstance(typeArgument); - })); + settingsUtils.Setup(x => x.GetSettings(It.IsAny(), It.IsAny())) + .Returns(new T()); return settingsUtils; } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/BasePTSettingsTest.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/BasePTSettingsTest.cs index 2bec4be724..023ee7e851 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/BasePTSettingsTest.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/BasePTSettingsTest.cs @@ -3,15 +3,26 @@ // See the LICENSE file in the project root for more information. using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.PowerToys.Settings.UI.Lib.Interface; namespace Microsoft.PowerToys.Settings.UnitTest { - public class BasePTSettingsTest : BasePTModuleSettings + public class BasePTSettingsTest : BasePTModuleSettings, ISettingsConfig { public BasePTSettingsTest() { Name = string.Empty; Version = string.Empty; } + + public string GetModuleName() + { + return Name; + } + + public bool UpgradeSettingsConfiguration() + { + return false; + } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/SettingsRepositoryTest.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/SettingsRepositoryTest.cs new file mode 100644 index 0000000000..67aa719da8 --- /dev/null +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/SettingsRepositoryTest.cs @@ -0,0 +1,68 @@ +// 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.IO; +using System.Threading.Tasks; +using Microsoft.PowerToys.Settings.UI.Lib; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; + +namespace CommonLibTest +{ + [TestClass] + public class SettingsRepositoryTest + { + private Task> GetSettingsRepository(ISettingsUtils settingsUtils) + { + + return Task.Run(() => + { + return SettingsRepository.GetInstance(settingsUtils); + }); + } + + + [TestMethod] + public void SettingsRepositoryInstanceWhenCalledMustReturnSameObject() + { + // The singleton class Settings Repository must always have a single instance + var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + + // Arrange and Act + SettingsRepository firstInstance = SettingsRepository.GetInstance(mockSettingsUtils.Object); + SettingsRepository secondInstance = SettingsRepository.GetInstance(mockSettingsUtils.Object); + + // Assert + Assert.IsTrue(object.ReferenceEquals(firstInstance, secondInstance)); + } + + [TestMethod] + public void SettingsRepositoryInstanceMustBeTheSameAcrossThreads() + { + // Multiple tasks try to access and initialize the settings repository class, however they must all access the same settings Repository object. + + // Arrange + var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + List>> settingsRepoTasks = new List>>(); + int numberOfTasks = 100; + + for(int i = 0; i < numberOfTasks; i++) + { + settingsRepoTasks.Add(GetSettingsRepository(mockSettingsUtils.Object)); + } + + // Act + Task.WaitAll(settingsRepoTasks.ToArray()); + + // Assert + for(int i=0; i< numberOfTasks-1; i++) + { + Assert.IsTrue(object.ReferenceEquals(settingsRepoTasks[i].Result, settingsRepoTasks[i + 1].Result)); + } + + } + } +} diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs index 0d31233992..4cb73efd6a 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs @@ -2,19 +2,13 @@ // 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.IO; -using System.Text.Json; -using Microsoft.PowerToys.Settings.UI.Lib; -using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; -using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; namespace ViewModelTests { [TestClass] public class ColorPicker { - + } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/FancyZones.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/FancyZones.cs index a4e1a4984a..c4cb98925e 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/FancyZones.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/FancyZones.cs @@ -20,6 +20,17 @@ namespace ViewModelTests { public const string FancyZonesTestFolderName = "Test\\FancyZones"; + private Mock mockGeneralSettingsUtils; + + private Mock mockFancyZonesSettingsUtils; + + [TestInitialize] + public void SetUp_StubSettingUtils() + { + mockGeneralSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + mockFancyZonesSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + } + [TestMethod] public void IsEnabled_ShouldDisableModule_WhenSuccessful() { @@ -31,7 +42,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsTrue(viewModel.IsEnabled); // check if the module is enabled. // act @@ -50,7 +61,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsTrue(viewModel.ShiftDrag); // check if value was initialized to false. // act @@ -69,7 +80,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.OverrideSnapHotkeys); // check if value was initialized to false. // act @@ -88,7 +99,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.MoveWindowsBasedOnPosition); // check if value was initialized to false. // act @@ -107,7 +118,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.MakeDraggedWindowsTransparent); // check if value was initialized to false. // act @@ -126,7 +137,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.MouseSwitch); // check if value was initialized to false. // act @@ -145,7 +156,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.DisplayChangeMoveWindows); // check if value was initialized to false. // act @@ -164,7 +175,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.ZoneSetChangeMoveWindows); // check if value was initialized to false. // act @@ -183,7 +194,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.AppLastZoneMoveWindows); // check if value was initialized to false. // act @@ -201,7 +212,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.OpenWindowOnActiveMonitor); // check if value was initialized to false. // act @@ -220,7 +231,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.RestoreSize); // check if value was initialized to false. // act @@ -239,7 +250,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsTrue(viewModel.UseCursorPosEditorStartupScreen); // check if value was initialized to false. // act @@ -258,7 +269,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.IsFalse(viewModel.ShowOnAllMonitors); // check if value was initialized to false. // act @@ -277,7 +288,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.AreEqual(ConfigDefaults.DefaultFancyZonesZoneHighlightColor, viewModel.ZoneHighlightColor); // act @@ -296,7 +307,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.AreEqual(ConfigDefaults.DefaultFancyzonesBorderColor, viewModel.ZoneBorderColor); // act @@ -315,7 +326,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.AreEqual(ConfigDefaults.DefaultFancyZonesInActiveColor, viewModel.ZoneInActiveColor); // act @@ -334,7 +345,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.AreEqual(string.Empty, viewModel.ExcludedApps); // act @@ -353,7 +364,7 @@ namespace ViewModelTests }; // arrange - FancyZonesViewModel viewModel = new FancyZonesViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, FancyZonesTestFolderName); + FancyZonesViewModel viewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockFancyZonesSettingsUtils.Object), SendMockIPCConfigMSG, FancyZonesTestFolderName); Assert.AreEqual(50, viewModel.HighlightOpacity); // act diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/General.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/General.cs index f696f24f97..04f724c131 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/General.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/General.cs @@ -8,6 +8,7 @@ using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Lib; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Moq; using NuGet.Frameworks; @@ -18,6 +19,14 @@ namespace ViewModelTests { public const string generalSettings_file_name = "Test\\GenealSettings"; + private Mock mockGeneralSettingsUtils; + + [TestInitialize] + public void SetUp_StubSettingUtils() + { + mockGeneralSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + } + [TestMethod] public void IsElevated_ShouldUpdateRunasAdminStatusAttrs_WhenSuccessful() { @@ -27,6 +36,7 @@ namespace ViewModelTests Func SendCheckForUpdatesIPCMessage = msg => { return 0; }; GeneralViewModel viewModel = new GeneralViewModel( new Mock().Object, + SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), "GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsUserText", false, @@ -64,6 +74,7 @@ namespace ViewModelTests Func SendCheckForUpdatesIPCMessage = msg => { return 0; }; GeneralViewModel viewModel = new GeneralViewModel( new Mock().Object, + SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), "GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsUserText", false, @@ -96,6 +107,7 @@ namespace ViewModelTests // Arrange GeneralViewModel viewModel = new GeneralViewModel( new Mock().Object, + SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), "GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsUserText", false, @@ -129,6 +141,7 @@ namespace ViewModelTests Func SendCheckForUpdatesIPCMessage = msg => { return 0; }; viewModel = new GeneralViewModel( new Mock().Object, + SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), "GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsUserText", false, @@ -160,6 +173,7 @@ namespace ViewModelTests Func SendCheckForUpdatesIPCMessage = msg => { return 0; }; GeneralViewModel viewModel = new GeneralViewModel( new Mock().Object, + SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), "GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsUserText", false, diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ImageResizer.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ImageResizer.cs index 0e4ef3e16f..c966b48153 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ImageResizer.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ImageResizer.cs @@ -18,13 +18,23 @@ namespace ViewModelTests [TestClass] public class ImageResizer { - public const string Module = "ImageResizer"; + // To have a consistent name. + public const string Module = ImageResizerSettings.ModuleName; + + private Mock mockGeneralSettingsUtils; + + private Mock mockImgResizerSettingsUtils; + + [TestInitialize] + public void SetUp_StubSettingUtils() + { + mockGeneralSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + mockImgResizerSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + } [TestMethod] public void IsEnabled_ShouldEnableModule_WhenSuccessful() { - var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); - // Assert Func SendMockIPCConfigMSG = msg => { @@ -34,7 +44,7 @@ namespace ViewModelTests }; // arrange - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils.Object, SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockImgResizerSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); // act viewModel.IsEnabled = true; @@ -47,13 +57,13 @@ namespace ViewModelTests var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); // act viewModel.JPEGQualityLevel = 10; // Assert - viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); + viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); Assert.AreEqual(10, viewModel.JPEGQualityLevel); } @@ -64,13 +74,13 @@ namespace ViewModelTests var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); // act viewModel.PngInterlaceOption = 10; // Assert - viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); + viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); Assert.AreEqual(10, viewModel.PngInterlaceOption); } @@ -81,13 +91,13 @@ namespace ViewModelTests var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); // act viewModel.TiffCompressOption = 10; // Assert - viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); + viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); Assert.AreEqual(10, viewModel.TiffCompressOption); } @@ -98,14 +108,14 @@ namespace ViewModelTests var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); string expectedValue = "%1 (%3)"; // act viewModel.FileName = expectedValue; // Assert - viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); + viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); Assert.AreEqual(expectedValue, viewModel.FileName); } @@ -113,7 +123,7 @@ namespace ViewModelTests public void KeepDateModified_ShouldUpdateValue_WhenSuccessful() { // arrange - var settingUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + var settingUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); var expectedSettingsString = new ImageResizerSettings() { Properties = new ImageResizerProperties() { ImageresizerKeepDateModified = new BoolProperty() { Value = true } } }.ToJsonString(); settingUtils.Setup(x => x.SaveSettings( @@ -123,7 +133,7 @@ namespace ViewModelTests .Verifiable(); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(settingUtils.Object, SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(settingUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); // act viewModel.KeepDateModified = true; @@ -139,13 +149,13 @@ namespace ViewModelTests var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); // act viewModel.Encoder = 3; // Assert - viewModel = new ImageResizerViewModel(mockSettingsUtils, SendMockIPCConfigMSG); + viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); Assert.AreEqual("163bcc30-e2e9-4f0b-961d-a3e9fdb788a3", viewModel.GetEncoderGuid(viewModel.Encoder)); Assert.AreEqual(3, viewModel.Encoder); } @@ -154,9 +164,9 @@ namespace ViewModelTests public void AddRow_ShouldAddEmptyImageSize_WhenSuccessful() { // arrange - var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils.Object, SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); int sizeOfOriginalArray = viewModel.Sizes.Count; // act @@ -170,9 +180,9 @@ namespace ViewModelTests public void DeleteImageSize_ShouldDeleteImageSize_WhenSuccessful() { // arrange - var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils.Object, SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); int sizeOfOriginalArray = viewModel.Sizes.Count; ImageSize deleteCandidate = viewModel.Sizes.Where(x => x.Id == 0).First(); diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/KeyboardManager.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/KeyboardManager.cs index 970fe95512..9c730eba9a 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/KeyboardManager.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/KeyboardManager.cs @@ -13,7 +13,7 @@ namespace ViewModelTests [TestClass] public class KeyboardManager { - public const string Module = "Keyboard Manager"; + public const string Module = KeyboardManagerSettings.ModuleName; [TestInitialize] public void Setup() diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs index 7833d2ff7e..6b3a78ac2a 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs @@ -18,6 +18,14 @@ namespace ViewModelTests { public const string Module = "Test\\File Explorer"; + private Mock mockPowerPreviewSettingsUtils; + + [TestInitialize] + public void SetUp_StubSettingUtils() + { + mockPowerPreviewSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + } + [TestMethod] public void SVGRenderIsEnabled_ShouldPrevHandler_WhenSuccessful() { @@ -30,7 +38,7 @@ namespace ViewModelTests }; // arrange - PowerPreviewViewModel viewModel = new PowerPreviewViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, Module); + PowerPreviewViewModel viewModel = new PowerPreviewViewModel(SettingsRepository.GetInstance(mockPowerPreviewSettingsUtils.Object), SendMockIPCConfigMSG, Module); // act viewModel.SVGRenderIsEnabled = true; @@ -48,7 +56,7 @@ namespace ViewModelTests }; // arrange - PowerPreviewViewModel viewModel = new PowerPreviewViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, Module); + PowerPreviewViewModel viewModel = new PowerPreviewViewModel(SettingsRepository.GetInstance(mockPowerPreviewSettingsUtils.Object), SendMockIPCConfigMSG, Module); // act viewModel.SVGThumbnailIsEnabled = true; @@ -66,7 +74,7 @@ namespace ViewModelTests }; // arrange - PowerPreviewViewModel viewModel = new PowerPreviewViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, Module);; + PowerPreviewViewModel viewModel = new PowerPreviewViewModel(SettingsRepository.GetInstance(mockPowerPreviewSettingsUtils.Object), SendMockIPCConfigMSG, Module); ; // act viewModel.MDRenderIsEnabled = true; diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerRename.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerRename.cs index 9f9a9d809e..41b614732e 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerRename.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerRename.cs @@ -16,9 +16,20 @@ namespace ViewModelTests [TestClass] public class PowerRename { - public const string ModuleName = "PowerRename"; + public const string ModuleName = PowerRenameSettings.ModuleName; public const string generalSettings_file_name = "Test\\PowerRename"; + private Mock mockGeneralSettingsUtils; + + private Mock mockPowerRenamePropertiesUtils; + + [TestInitialize] + public void SetUp_StubSettingUtils() + { + mockGeneralSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + mockPowerRenamePropertiesUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + } + [TestMethod] public void IsEnabled_ShouldEnableModule_WhenSuccessful() { @@ -31,7 +42,7 @@ namespace ViewModelTests }; // arrange - PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(mockPowerRenamePropertiesUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG, generalSettings_file_name); // act viewModel.IsEnabled = true; @@ -49,7 +60,7 @@ namespace ViewModelTests }; // arrange - PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(mockPowerRenamePropertiesUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG, generalSettings_file_name); // act viewModel.MRUEnabled = true; @@ -59,7 +70,7 @@ namespace ViewModelTests public void WhenIsEnabledIsOffAndMRUEnabledIsOffGlobalAndMruShouldBeOff() { Func SendMockIPCConfigMSG = msg => { return 0; }; - PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(mockPowerRenamePropertiesUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG, generalSettings_file_name); viewModel.IsEnabled = false; viewModel.MRUEnabled = false; @@ -71,7 +82,7 @@ namespace ViewModelTests public void WhenIsEnabledIsOffAndMRUEnabledIsOnGlobalAndMruShouldBeOff() { Func SendMockIPCConfigMSG = msg => { return 0; }; - PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(mockPowerRenamePropertiesUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG, generalSettings_file_name); viewModel.IsEnabled = false; viewModel.MRUEnabled = true; @@ -83,7 +94,7 @@ namespace ViewModelTests public void WhenIsEnabledIsOnAndMRUEnabledIsOffGlobalAndMruShouldBeOff() { Func SendMockIPCConfigMSG = msg => { return 0; }; - PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(mockPowerRenamePropertiesUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG, generalSettings_file_name); viewModel.IsEnabled = true; viewModel.MRUEnabled = false; @@ -95,7 +106,7 @@ namespace ViewModelTests public void WhenIsEnabledIsOnAndMRUEnabledIsOnGlobalAndMruShouldBeOn() { Func SendMockIPCConfigMSG = msg => { return 0; }; - PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(mockPowerRenamePropertiesUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG, generalSettings_file_name); viewModel.IsEnabled = true; viewModel.MRUEnabled = true; @@ -115,7 +126,7 @@ namespace ViewModelTests }; // arrange - PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(mockPowerRenamePropertiesUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG, generalSettings_file_name); // act viewModel.EnabledOnContextMenu = true; @@ -133,7 +144,7 @@ namespace ViewModelTests }; // arrange - PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(mockPowerRenamePropertiesUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG, generalSettings_file_name); // act viewModel.EnabledOnContextMenu = true; @@ -151,7 +162,7 @@ namespace ViewModelTests }; // arrange - PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(mockPowerRenamePropertiesUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG, generalSettings_file_name); // act viewModel.RestoreFlagsOnLaunch = true; @@ -169,7 +180,7 @@ namespace ViewModelTests }; // arrange - PowerRenameViewModel viewModel = new PowerRenameViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, generalSettings_file_name); + PowerRenameViewModel viewModel = new PowerRenameViewModel(mockPowerRenamePropertiesUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG, generalSettings_file_name); // act viewModel.MaxDispListNum = 20; diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs index d788154ffd..24928c1006 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs @@ -18,6 +18,17 @@ namespace ViewModelTests { public const string ShortCutGuideTestFolderName = "Test\\ShortCutGuide"; + private Mock mockGeneralSettingsUtils; + + private Mock mockShortcutGuideSettingsUtils; + + [TestInitialize] + public void SetUp_StubSettingUtils() + { + mockGeneralSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + mockShortcutGuideSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + } + [TestMethod] public void IsEnabled_ShouldEnableModule_WhenSuccessful() { @@ -31,7 +42,7 @@ namespace ViewModelTests }; // Arrange - ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, ShortCutGuideTestFolderName); + ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockShortcutGuideSettingsUtils.Object), SendMockIPCConfigMSG, ShortCutGuideTestFolderName); // Act viewModel.IsEnabled = true; @@ -50,7 +61,7 @@ namespace ViewModelTests }; // Arrange - ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, ShortCutGuideTestFolderName); + ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockShortcutGuideSettingsUtils.Object), SendMockIPCConfigMSG, ShortCutGuideTestFolderName); Assert.AreEqual(1, viewModel.ThemeIndex); // Act @@ -70,7 +81,7 @@ namespace ViewModelTests }; // Arrange - ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, ShortCutGuideTestFolderName); + ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockShortcutGuideSettingsUtils.Object), SendMockIPCConfigMSG, ShortCutGuideTestFolderName); Assert.AreEqual(900, viewModel.PressTime); // Act @@ -92,7 +103,7 @@ namespace ViewModelTests }; // Arrange - ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SendMockIPCConfigMSG, ShortCutGuideTestFolderName); + ShortcutGuideViewModel viewModel = new ShortcutGuideViewModel(SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SettingsRepository.GetInstance(mockShortcutGuideSettingsUtils.Object), SendMockIPCConfigMSG, ShortCutGuideTestFolderName); Assert.AreEqual(90, viewModel.OverlayOpacity); // Act diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Converters/ModuleEnabledToForegroundConverter.cs b/src/core/Microsoft.PowerToys.Settings.UI/Converters/ModuleEnabledToForegroundConverter.cs index 72cf1129c4..415fa97ba7 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Converters/ModuleEnabledToForegroundConverter.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Converters/ModuleEnabledToForegroundConverter.cs @@ -15,15 +15,15 @@ namespace Microsoft.PowerToys.Settings.UI.Converters { private readonly ISettingsUtils settingsUtils = new SettingsUtils(new SystemIOProvider()); + private string selectedTheme = string.Empty; + public object Convert(object value, Type targetType, object parameter, string language) { bool isEnabled = (bool)value; - GeneralSettings generalSettings = settingsUtils.GetSettings(string.Empty); var defaultTheme = new Windows.UI.ViewManagement.UISettings(); var uiTheme = defaultTheme.GetColorValue(Windows.UI.ViewManagement.UIColorType.Background).ToString(); - - string selectedTheme = generalSettings.Theme.ToLower(); + selectedTheme = SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Theme.ToLower(); if (selectedTheme == "dark" || (selectedTheme == "system" && uiTheme == "#FF000000")) { diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs index 6e47d7c8cf..929fc9398c 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs @@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public ColorPickerPage() { var settingsUtils = new SettingsUtils(new SystemIOProvider()); - ViewModel = new ColorPickerViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage); + ViewModel = new ColorPickerViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; InitializeComponent(); } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml.cs index 56b513e517..e022e63fd9 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml.cs @@ -17,7 +17,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views { InitializeComponent(); var settingsUtils = new SettingsUtils(new SystemIOProvider()); - ViewModel = new FancyZonesViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage); + ViewModel = new FancyZonesViewModel(SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml.cs index 459679c753..1546469ec2 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml.cs @@ -37,6 +37,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views ViewModel = new GeneralViewModel( settingsUtils, + SettingsRepository.GetInstance(settingsUtils), loader.GetString("GeneralSettings_RunningAsAdminText"), loader.GetString("GeneralSettings_RunningAsUserText"), ShellPage.IsElevated, diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml.cs index 7f8281679d..b98c2d706d 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml.cs @@ -17,9 +17,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views public ImageResizerPage() { InitializeComponent(); - var settingsUtils = new SettingsUtils(new SystemIOProvider()); - ViewModel = new ImageResizerViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage); + ViewModel = new ImageResizerViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/KeyboardManagerPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/KeyboardManagerPage.xaml.cs index 2333badd6b..2220c43804 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/KeyboardManagerPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/KeyboardManagerPage.xaml.cs @@ -32,7 +32,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views dispatcher = Window.Current.Dispatcher; var settingsUtils = new SettingsUtils(new SystemIOProvider()); - ViewModel = new KeyboardManagerViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage, FilterRemapKeysList); + ViewModel = new KeyboardManagerViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, FilterRemapKeysList); watcher = Helper.GetFileWatcher( PowerToyName, diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml.cs index b41fa6f596..681af560bb 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml.cs @@ -22,7 +22,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views { InitializeComponent(); var settingsUtils = new SettingsUtils(new SystemIOProvider()); - ViewModel = new PowerLauncherViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage, (int)Windows.System.VirtualKey.Space); + ViewModel = new PowerLauncherViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, (int)Windows.System.VirtualKey.Space); DataContext = ViewModel; var loader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView(); diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml.cs index fe71b790a4..5fcec859d3 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml.cs @@ -20,7 +20,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views { InitializeComponent(); var settingsUtils = new SettingsUtils(new SystemIOProvider()); - ViewModel = new PowerPreviewViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage); + ViewModel = new PowerPreviewViewModel(SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerRenamePage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerRenamePage.xaml.cs index 7f44b3672a..ff1a20be02 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerRenamePage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerRenamePage.xaml.cs @@ -17,7 +17,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views { InitializeComponent(); var settingsUtils = new SettingsUtils(new SystemIOProvider()); - ViewModel = new PowerRenameViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage); + ViewModel = new PowerRenameViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/ShellPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/ShellPage.xaml.cs index eb61947599..972a1595ca 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ShellPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ShellPage.xaml.cs @@ -2,7 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; +using Microsoft.PowerToys.Settings.UI.Lib; using Microsoft.PowerToys.Settings.UI.ViewModels; using Windows.Data.Json; using Windows.UI.Xaml.Controls; diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/ShortcutGuidePage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/ShortcutGuidePage.xaml.cs index c968dc8cd9..2b0dec506d 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ShortcutGuidePage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ShortcutGuidePage.xaml.cs @@ -16,8 +16,9 @@ namespace Microsoft.PowerToys.Settings.UI.Views public ShortcutGuidePage() { InitializeComponent(); + var settingsUtils = new SettingsUtils(new SystemIOProvider()); - ViewModel = new ShortcutGuideViewModel(settingsUtils, ShellPage.SendDefaultIPCMessage); + ViewModel = new ShortcutGuideViewModel(SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } } From dafc1e0c7d0226b54b6a51c0b8381507fb863a76 Mon Sep 17 00:00:00 2001 From: Alekhya Date: Wed, 23 Sep 2020 13:20:58 -0700 Subject: [PATCH 13/22] Fixed scaling Accessibility issues in Settings (#6774) * fixed scaling issues in settings * reduced the size to 375 from 380 * Set scrollviewer visibility to auto so that it is visible only when needed --- .../Views/FancyZonesPage.xaml | 2 +- .../Views/ImageResizerPage.xaml | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml index e4d4cf7c09..8775ea56bd 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml @@ -285,7 +285,7 @@ Margin="{StaticResource SmallTopMargin}" Text="{x:Bind Mode=TwoWay, Path=ViewModel.ExcludedApps}" IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}" - Width="380" + Width="375" Height="160" HorizontalAlignment="Left" ScrollViewer.VerticalScrollBarVisibility ="Visible" diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml index 16f1ae11e4..3939cea8ec 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml @@ -62,7 +62,10 @@ ItemsSource="{x:Bind ViewModel.Sizes, Mode=TwoWay}" Padding="0" IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}" - SelectionMode="None"> + SelectionMode="None" + ScrollViewer.HorizontalScrollMode="Enabled" + ScrollViewer.HorizontalScrollBarVisibility="Auto" + ScrollViewer.IsHorizontalRailEnabled="True">