diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index bf6e10b067..35ab18554f 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,15 +1,15 @@ blank_issues_enabled: false contact_links: - - name: "\U0001F4F7 VideoConference" + - name: "\U0001F4F7 Video Conference Mute Issue" url: https://github.com/microsoft/PowerToys/issues/6246 - about: Report Bug for the VideoConference Power Toy + about: Report Bug for the Video Conference Mute utiltity - name: "\U0001F6A8 Microsoft Security Response Center (MSRC)" url: https://msrc.microsoft.com/create-report about: Report security bugs - name: "\U0001F4DA PowerToys user documentation" url: https://github.com/microsoft/PowerToys/wiki - about: Documentation for users of PowerToys + about: Documentation for users of PowerToys utilities - name: "\U0001F4DA PowerToys dev documentation" url: https://github.com/microsoft/PowerToys/tree/master/doc/devdocs - about: Documentation for people interested in developing for PowerToys + about: Documentation for people interested in developing and contributing for PowerToys diff --git a/.github/ISSUE_TEMPLATE/translation_issue.md b/.github/ISSUE_TEMPLATE/translation_issue.md new file mode 100644 index 0000000000..edb1634575 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/translation_issue.md @@ -0,0 +1,30 @@ +--- +name: 📖 Localization/Translation issue +about: Report incorrect translations. +title: '' +labels: Issue-Bug,Area-Localization +assignees: '' + +--- + +## ℹ Computer information + +- PowerToys version: +- PowerToy utility: +- Language: + +## 📝 Provide where the issue is / 📷 Screenshots + +_Are there any useful screenshots? WinKey+Shift+S and then just paste them directly into the form_ + +### ❌ Actual phrase(s) + +_What is there?_ + +### ✔️ Expected phrase(s) + +_What do you expect?_ + +### ℹ Why is the current translation wrong + +_If it is opinion based issue, why do you feel this is incorrect? Example: term is outdated_ diff --git a/README.md b/README.md index 988b02037d..f33f6e0acf 100644 --- a/README.md +++ b/README.md @@ -10,17 +10,15 @@ For a video overview of PowerToys, including install steps and a walkthrough of ## Build status -| Branch | Status x64 | -|---|---| -| Master | [![Build Status for Master](https://dev.azure.com/ms/PowerToys/_apis/build/status/microsoft.PowerToys?branchName=master)](https://dev.azure.com/ms/PowerToys/_build/latest?definitionId=219&branchName=master) | -| Stable | [![Build Status for Stable](https://dev.azure.com/ms/PowerToys/_apis/build/status/microsoft.PowerToys?branchName=stable)](https://dev.azure.com/ms/PowerToys/_build/latest?definitionId=219&branchName=stable) | -| Installer | [![Build Status for Installer](https://github-private.visualstudio.com/microsoft/_apis/build/status/CDPX/powertoys/powertoys-Windows-Official-master-Test?branchName=master)](https://github-private.visualstudio.com/microsoft/_build/latest?definitionId=61&branchName=master) | +| Architecture | Master | Stable | Installer | +|--------------|--------|--------|-----------| +| x64 | [![Build Status for Master](https://dev.azure.com/ms/PowerToys/_apis/build/status/microsoft.PowerToys?branchName=master)](https://dev.azure.com/ms/PowerToys/_build/latest?definitionId=219&branchName=master) | [![Build Status for Stable](https://dev.azure.com/ms/PowerToys/_apis/build/status/microsoft.PowerToys?branchName=stable)](https://dev.azure.com/ms/PowerToys/_build/latest?definitionId=219&branchName=stable) | [![Build Status for Installer](https://github-private.visualstudio.com/microsoft/_apis/build/status/CDPX/powertoys/powertoys-Windows-Official-master-Test?branchName=master)](https://github-private.visualstudio.com/microsoft/_build/latest?definitionId=61&branchName=master) | ## Current PowerToy Utilities ### Color Picker -[](https://aka.ms/PowerToysOverview_ColorPicker) [ColorPicker](https://aka.ms/PowerToysOverview_ColorPicker) is a simple and quick system-wide color picker with Win+Shift+C. Color Picker allows to pick colors from any currently running application and automatically copies the HEX or RGB values to your clipboard. This code is based on [Martin Chrzan's Color Picker](https://github.com/martinchrzan/ColorPicker). +[](https://aka.ms/PowerToysOverview_ColorPicker) [Color Picker](https://aka.ms/PowerToysOverview_ColorPicker) is a simple and quick system-wide color picker with Win+Shift+C. Color Picker allows to pick colors from any currently running application and automatically copies the HEX or RGB values to your clipboard. This code is based on [Martin Chrzan's Color Picker](https://github.com/martinchrzan/ColorPicker).


@@ -90,16 +88,13 @@ Preview Pane is an existing feature in the File Explorer. To enable it, you jus ## Installing and running Microsoft PowerToys #### Requirements + - Windows 10 1803 (build 17134) or later. - Have [.NET Core 3.1 Desktop Runtime](https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-desktop-3.1.4-windows-x64-installer). The installer should handle this but we want to directly make people aware. -#### 0.18 users for updating via notifications - -- We adjusted how upgrading works in 0.20. In 0.19 we accounted for this upcoming change but if you are going from 0.18 to 0.21, please directly use the installer file. - ### Via GitHub with EXE [Recommended] -Install from the [Microsoft PowerToys GitHub releases page][github-release-link]. Click on `Assets` to show the files available in the release and then click on `PowerToysSetup-0.23.2-x64.exe` to download the PowerToys installer. +Install from the [Microsoft PowerToys GitHub releases page][github-release-link]. Click on `Assets` to show the files available in the release and then click on `PowerToysSetup-0.25.0-x64.exe` to download the PowerToys installer. This is our preferred method. @@ -110,10 +105,12 @@ Download PowerToys from [WinGet](https://github.com/microsoft/winget-cli/release WinGet install powertoys ``` -### Experiential PowerToys utility with Video conference muting +### Experimental PowerToys utility with Video conference muting Install the [pre-release experimental version of PowerToys][github-prerelease-link] to try out this version. It includes all improvements from 0.23 in addition to the Video conference utility. Click on `Assets` to show the files available in the release and then download the .exe installer. +**Note:** We'll have 0.26 Experimental out in the first week of November 2020. + ### Other install methods There are [community driven install methods](./doc/unofficalInstallMethods.md) such as Chocolatey and Scoop. If these are your preferred install solutions, this will have the install instructions. @@ -132,81 +129,61 @@ We currently support the matrix below. ## What's Happening -### September 2020 Update +### October 2020 Update -Our goals for 0.23 release cycle was to focus on stability, accessibility, localization and quality of life improvements for both the development team and our end users. We have a full accessibility pass being done starting end of September to audit all of PowerToys. Our localization efforts now had data flowing both directions as well. +Our goals for [0.25 release cycle][github-release-link] was to focus on stability, accessibility, localization and quality of life improvements for both the development team and our end users. Our first end to end localization pass has been done. We know it isn't perfect but we are in 17 languages now. If you find an issue, please file a [localization bug][loc-bug]. Our [prioritized roadmap][roadmap] of features and utilities that the core team is focusing on. -#### Highlights from September - -- We shipped [v0.23][github-release-link]! (0.24 Experimental build coming shortly) +#### Highlights from October 2020 **General** +- First pass on localization complete. 17 different languages. We know there will be some rough areas, please [make us aware so we can correct them][loc-bug]. +- Logging added into the installer +- Large sums of accessibility issues fixed. +- Less notifications for installing +- FxCop work is almost fully wrapped up +- Wrapped up the [Video GIF capture spec](https://github.com/microsoft/PowerToys/wiki/Video-GIF-Capture). This is laying out our plan for the future work. -- Localization pipeline is flowing from our Github to the loc system and back. 0.25 should be localized now. -- The EXE installer should be at parity now with the MSI. Please go to the wiki for [installer args](https://github.com/microsoft/PowerToys/wiki/Installer-arguments-for-exe) +**Color Picker** +- Additional color style selections such as CYMK and HSL **FancyZones** - -- Fixed bug on not seeing a newly attached screen -- Fixed spanning across monitors bug -- Added in default layout for new users, a Priority Grid -- Added keyboard support to grow / shrink to multiple zones -- General bug fixes - -**PT Run** - -- Multiple crash bugs fixed. Prioritized any users reported along with top hits from Watson reporting -- Stopped PT Run from interfering with an install -- Fixed folder bug if it had a # in it (Thanks @jjw24 for the PR!) -- Fixed a screen flicker for -- General bug fixes -- Allow Command Line args in PowerToys Run (Thanks @@royvou) +- Multiple bugs fixed +- Better zone drawing improvements **Keyboard manager** +- Fixed terminal input map failure +- Better app compat +- Multiple bug fixes +- Ability to directly disable keys/shortcuts -- Multiple crash bugs fixed. Prioritized any users reported along with top hits from Watson reporting -- Fixed multiple accessibility issues. -- General bug fixes +**PowerToys Run** +- expanded environment var searching such as %windr% +- multiple crash bug fixes +- Improvements on calculator plugin +- Directly able to override theming +- Windows will open to what shell you want +- Better action key support + - `=` for direct calculator + - `?` for direct file searching + - `.` for direct for applications + - `//` for direct URL + - `<` for running processes + - `>` for shell processes -**Preview Pane** +**Dev docs** +- Added multiple developer related docs. -- Added in Frontmatter and better (but still basic) latex support. +I'd like to directly call out [@p-storm](https://github.com/p-storm), [@TobiasSekan](https://github.com/TobiasSekan), [@davidegiacometti](https://github.com/davidegiacometti), [@royvou](https://github.com/royvou), [@gordonwatts](https://github.com/gordonwatts), [@Aaron-Junker](https://github.com/Aaron-Junker), [@htcfreek](https://github.com/htcfreek) and [@niels9001](https://github.com/niels9001) for their continued community support and helping directly make PowerToys a better piece of software. -**Settings** +### What is being planned for 0.27 - November 2020 -- Fixed scaling issue for responsive design on Image Resizer -- Fixed crash on empty color value. -- Fixed crash for toggling FancyZones on/off -- Fixed 0x00 NFTS crash for settings -- Fixed multiple accessibility issues. -- Layout adjustments (Thanks @niels9001) -- General bug fixes - -**Dev related** - -- FxCop is being rolled out across all PowerToys. This should catch a lot of possible leaks. -- Unified PT Run's log system -- PT Run's calc plugin now has unit tests (Thanks @P-Storm) -- Dev setup install script now supports VS preview (Thanks @TobiasSekan) -- @CaelestisZ, @kameshkotwani, @adriancampos, @RahulDas782 for doc tweaks -- Thanks @Aaron-Junker, @jay-o-way and @htcfreek for helping triage! -- Thanks for everyone that filled an issue. It really does help us prioritize - -#### Video / GIF capture functional spec for public review - -Deondre Davis created our [functional spec for creating a light weight, video / GIF recording tool](https://github.com/microsoft/PowerToys/pull/6900). We encourage everyone to review it and please leave comments in the pull request so we can adjust as needed. We'll be closing it for feedback on October 12th, 2020. - -This is for work [post-stabilization of current roadmap work](https://github.com/microsoft/PowerToys/wiki/Roadmap#post-stabilization) and is only the spec for what we are thinking about support. Just want to set expectations here. - -### What is being planned for 0.25 - -For [0.25](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F13), we are proactively working on: +For [0.27](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F14), we are proactively working on: - Stability -- Localization -- Improve interactions with elevated windows and keeping most of the PT utilities non-elevated so we still have a 'shell' like experience +- Accessibility +- Video conference mute - OOBE work ### PowerToys roadmap @@ -242,7 +219,8 @@ The application logs basic telemetry. Our Telemetry Data page (Coming Soon) has [oss-CLA]: https://cla.opensource.microsoft.com [oss-conduct-code]: CODE_OF_CONDUCT.md [github-release-link]: https://github.com/microsoft/PowerToys/releases/ -[github-prerelease-link]: https://github.com/microsoft/PowerToys/releases/tag/v0.24.0-Experimental +[github-prerelease-link]: https://github.com/microsoft/PowerToys/releases/tag/v0.24.0 [roadmap]: https://github.com/microsoft/PowerToys/wiki/Roadmap [privacyLink]: http://go.microsoft.com/fwlink/?LinkId=521839 [vidConfOverview]: https://aka.ms/PowerToysOverview_VideoConference +[loc-bug]: https://github.com/microsoft/PowerToys/issues/new?assignees=&labels=&template=translation_issue.md&title= diff --git a/doc/devdocs/modules/launcher/project_structure.md b/doc/devdocs/modules/launcher/project_structure.md index b76976d5c5..0a8ccbfe3c 100644 --- a/doc/devdocs/modules/launcher/project_structure.md +++ b/doc/devdocs/modules/launcher/project_structure.md @@ -16,9 +16,9 @@ This is the startup project for the `PowerToys Run.` It is a WPF desktop applica [`Wox.Core`](/src/modules/launcher/Wox.Core) is a .net core project that contains helper classes required by the `PowerLauncher` project. Two major functionalities encapsulated in this project are [`PluginManager`](/src/modules/launcher/Wox.Core/Plugin/PluginManager.cs) and [`Query Builder.`](/src/modules/launcher/Wox.Core/Plugin/QueryBuilder.cs) [`PluginManager`](/src/modules/launcher/Wox.Core/Plugin/PluginManager.cs) provides an interface for managing C# plugins. [`Query Builder.`](/src/modules/launcher/Wox.Core/Plugin/QueryBuilder.cs) decimate user-typed query string and creates a [`Query`](/src/modules/launcher/Wox.Plugin/Query.cs) object. [`Query`](/src/modules/launcher/Wox.Plugin/Query.cs) object contains the action keyword and cleaned query, which is then sent to all plugins. #### [`Wox.Infrastructure`](/src/modules/launcher/Wox.Infrastructure) -[`Wox.Infrastructure`](/src/modules/launcher/Wox.Infrastructure) is a .net core project that contains helper classes required for logging, image manipulation, and storage by the `PowerLauncher` project and the plugins. [`ImageLoader.cs`](/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs) class is used to load icons for `Win32` program. It also provides caching functionality to speed up image loading for frequently queried programs. [`Log.cs`](/src/modules/launcher/Wox.Infrastructure/Logger/Log.cs) provides an abstraction for logging error, information, and output to text files. These files are stored at `%userprofile%/appdata/local/microsoft/powertoys/powertoys run/Logs.` +[`Wox.Infrastructure`](/src/modules/launcher/Wox.Infrastructure) is a .net core project that contains helper classes required for image manipulation and storage by the `PowerLauncher` project and the plugins. [`ImageLoader.cs`](/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs) class is used to load icons for `Win32` program. It also provides caching functionality to speed up image loading for frequently queried programs. #### [`Wox.Plugin`](/src/modules/launcher/Wox.Plugin) -[`Wox.Plugin`](/src/modules/launcher/Wox.Plugin) contains interfaces that facilitate communication between `PowerLauncher` and plugins. These interfaces have been discussed in detail [here](/doc/devdocs/modules/launcher/architecture.md#flow-of-data-between-viewmodels-and-pluginsmodel). +[`Wox.Plugin`](/src/modules/launcher/Wox.Plugin) contains interfaces that facilitate communication between `PowerLauncher` and plugins. These interfaces have been discussed in detail [here](/doc/devdocs/modules/launcher/architecture.md#flow-of-data-between-viewmodels-and-pluginsmodel). It also contains a helper class for logging. [`Log.cs`](/src/modules/launcher/Wox.Plugin/Logger/Log.cs) provides an abstraction for logging error, information, and output to text files. These files are stored at `%userprofile%/appdata/local/microsoft/powertoys/powertoys run/Logs.` diff --git a/installer/PowerToysBootstrapper/bootstrapper/Resources.resx b/installer/PowerToysBootstrapper/bootstrapper/Resources.resx index d1ca376f1a..e80ed5a84a 100644 --- a/installer/PowerToysBootstrapper/bootstrapper/Resources.resx +++ b/installer/PowerToysBootstrapper/bootstrapper/Resources.resx @@ -64,4 +64,94 @@ PowerToys installation error + + An update to PowerToys is available. + + + PowerToys download started. + + + An update to PowerToys is ready to install. + + + Error: couldn't download PowerToys installer. Visit our GitHub page to update. + + + Update now + + + At next launch + + + Error: please uninstall the previous version of PowerToys manually. + + + An update to PowerToys is available. Visit our GitHub page to update. + + + PowerToys is up to date. + + + Visit + + + More info... + + + Abort + + + Click Snooze to be reminded in: + + + 1 day + + + 5 days + + + Downloading... + + + Download complete + + + PowerToys Update + + + We've detected a previous installation of PowerToys. Would you like to remove it? + + + PowerToys: uninstall previous version? + + + Couldn't extract MSI installer! + + + Extracting PowerToys MSI... + + + Uninstalling previous PowerToys version... + + + Couldn't uninstall previous PowerToys version! + + + Installing dotnet... + + + Couldn't install dotnet! + + + Installing new PowerToys version... + + + PowerToys installation complete! + + + Couldn't install new PowerToys version. + + + Snooze + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp b/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp index 2ab0c29cd5..85774cc741 100644 --- a/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp +++ b/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp @@ -14,11 +14,14 @@ extern "C" IMAGE_DOS_HEADER __ImageBase; +auto Strings = updating::notifications::strings::create(); + #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) -namespace +namespace // Strings in this namespace should not be localized { const wchar_t APPLICATION_ID[] = L"PowerToysInstaller"; + const wchar_t INSTALLATION_TOAST_TITLE[] = L"PowerToys Installation"; const wchar_t TOAST_TAG[] = L"PowerToysInstallerProgress"; const char LOG_FILENAME[] = "powertoys-bootstrapper-" STR(VERSION_MAJOR) "." STR(VERSION_MINOR) "." STR(VERSION_REVISION) ".log"; const char MSI_LOG_FILENAME[] = "powertoys-bootstrapper-msi-" STR(VERSION_MAJOR) "." STR(VERSION_MINOR) "." STR(VERSION_REVISION) ".log"; @@ -27,20 +30,6 @@ namespace #undef STR #undef STR_HELPER -namespace localized_strings -{ - const wchar_t INSTALLER_EXTRACT_ERROR[] = L"Couldn't extract MSI installer!"; - const wchar_t TOAST_TITLE[] = L"PowerToys Installation"; - const wchar_t EXTRACTING_INSTALLER[] = L"Extracting PowerToys MSI..."; - const wchar_t UNINSTALLING_PREVIOUS_VERSION[] = L"Uninstalling previous PowerToys version..."; - const wchar_t UNINSTALL_PREVIOUS_VERSION_ERROR[] = L"Couldn't uninstall previous PowerToys version!"; - const wchar_t INSTALLING_DOTNET[] = L"Installing dotnet..."; - const wchar_t DOTNET_INSTALL_ERROR[] = L"Couldn't install dotnet!"; - const wchar_t INSTALLING_NEW_VERSION[] = L"Installing new PowerToys version..."; - const wchar_t NEW_VERSION_INSTALLATION_DONE[] = L"PowerToys installation complete!"; - const wchar_t NEW_VERSION_INSTALLATION_ERROR[] = L"Couldn't install new PowerToys version."; -} - namespace fs = std::filesystem; std::optional extractEmbeddedInstaller() @@ -96,7 +85,6 @@ void setup_log(fs::path directory, const spdlog::level::level_enum severity) int bootstrapper() { - using namespace localized_strings; winrt::init_apartment(); cxxopts::Options options{ "PowerToysBootstrapper" }; // clang-format off @@ -252,7 +240,7 @@ int bootstrapper() iconPath = std::move(*extractedIcon); } spdlog::debug("Registering app id for toast notifications"); - notifications::register_application_id(TOAST_TITLE, iconPath.c_str()); + notifications::register_application_id(INSTALLATION_TOAST_TITLE, iconPath.c_str()); auto removeShortcut = wil::scope_exit([&] { notifications::unregister_application_id(); @@ -274,12 +262,12 @@ int bootstrapper() std::mutex progressLock; notifications::progress_bar_params progressParams; progressParams.progress = 0.0f; - progressParams.progress_title = EXTRACTING_INSTALLER; + progressParams.progress_title = GET_RESOURCE_STRING(IDS_EXTRACTING_INSTALLER); notifications::toast_params params{ TOAST_TAG, false, std::move(progressParams) }; if (!silent) { spdlog::debug("Launching progress toast notification"); - notifications::show_toast_with_activations({}, TOAST_TITLE, {}, {}, std::move(params)); + notifications::show_toast_with_activations({}, INSTALLATION_TOAST_TITLE, {}, {}, std::move(params)); } auto processToasts = wil::scope_exit([&] { @@ -322,7 +310,7 @@ int bootstrapper() { if (!silent) { - notifications::show_toast(INSTALLER_EXTRACT_ERROR, TOAST_TITLE); + notifications::show_toast(GET_RESOURCE_STRING(IDS_INSTALLER_EXTRACT_ERROR), INSTALLATION_TOAST_TITLE); } spdlog::error("Couldn't install the MSI installer ({})", GetLastError()); return 1; @@ -332,7 +320,7 @@ int bootstrapper() fs::remove(*installerPath, _); }); - updateProgressBar(.25f, UNINSTALLING_PREVIOUS_VERSION); + updateProgressBar(.25f, GET_RESOURCE_STRING(IDS_UNINSTALLING_PREVIOUS_VERSION).c_str()); spdlog::debug("Acquiring existing MSI package path"); const auto package_path = updating::get_msi_package_path(); if (!package_path.empty()) @@ -343,15 +331,15 @@ int bootstrapper() { spdlog::debug("Existing MSI package path not found"); } - if (!package_path.empty() && !updating::uninstall_msi_version(package_path) && !silent) + if (!package_path.empty() && !updating::uninstall_msi_version(package_path, Strings) && !silent) { spdlog::error("Couldn't install the existing MSI package ({})", GetLastError()); - notifications::show_toast(UNINSTALL_PREVIOUS_VERSION_ERROR, TOAST_TITLE); + notifications::show_toast(GET_RESOURCE_STRING(IDS_UNINSTALL_PREVIOUS_VERSION_ERROR), INSTALLATION_TOAST_TITLE); } const bool installDotnet = !skipDotnetInstall; if (installDotnet) { - updateProgressBar(.5f, INSTALLING_DOTNET); + updateProgressBar(.5f, GET_RESOURCE_STRING(IDS_INSTALLING_DOTNET).c_str()); } try @@ -365,7 +353,7 @@ int bootstrapper() !updating::install_dotnet(silent) && !silent) { - notifications::show_toast(DOTNET_INSTALL_ERROR, TOAST_TITLE); + notifications::show_toast(GET_RESOURCE_STRING(IDS_DOTNET_INSTALL_ERROR), INSTALLATION_TOAST_TITLE); } } } @@ -375,13 +363,14 @@ int bootstrapper() MessageBoxW(nullptr, L".NET Core installation", L"Unknown exception encountered!", MB_OK | MB_ICONERROR); } - updateProgressBar(.75f, INSTALLING_NEW_VERSION); + updateProgressBar(.75f, GET_RESOURCE_STRING(IDS_INSTALLING_NEW_VERSION).c_str()); // Always skip dotnet install, because we should've installed it from here earlier std::wstring msiProps = L"SKIPDOTNETINSTALL=1 "; spdlog::debug("Launching MSI installation for new package {}", installerPath->string()); const bool installationDone = MsiInstallProductW(installerPath->c_str(), msiProps.c_str()) == ERROR_SUCCESS; - updateProgressBar(1.f, installationDone ? NEW_VERSION_INSTALLATION_DONE : NEW_VERSION_INSTALLATION_ERROR); + updateProgressBar(1.f, + installationDone ? GET_RESOURCE_STRING(IDS_NEW_VERSION_INSTALLATION_DONE).c_str() : GET_RESOURCE_STRING(IDS_NEW_VERSION_INSTALLATION_ERROR).c_str()); if (!installationDone) { spdlog::error("Couldn't install new MSI package ({})", GetLastError()); diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/cs/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/cs/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..6bcc4709de --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/cs/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/de/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/de/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..0ceb815e69 --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/de/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/es/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/es/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..5f8875c61d --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/es/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/fr/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/fr/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..68c63c6898 --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/fr/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/hu/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/hu/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..170a038827 --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/hu/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/it/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/it/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..8d1be1be2c --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/it/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/ja/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/ja/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..0ea663700a --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/ja/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/ko/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/ko/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..cb0067fb68 --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/ko/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/nl/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/nl/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..732cf16cab --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/nl/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/pl/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/pl/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..f5a1997401 --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/pl/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/pt-BR/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/pt-BR/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..e34f883dce --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/pt-BR/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/pt-PT/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/pt-PT/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..5ff83a80d1 --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/pt-PT/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/ru/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/ru/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..841505881b --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/ru/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/sv/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/sv/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..506e8580a1 --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/sv/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/tr/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/tr/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..c80fbc0db4 --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/tr/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/zh-Hans/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/zh-Hans/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..a1d18ae8d6 --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/zh-Hans/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/loc/zh-Hant/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl b/installer/PowerToysBootstrapper/bootstrapper/loc/zh-Hant/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl new file mode 100644 index 0000000000..a8017c3c4b --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/loc/zh-Hant/installer/PowerToysBootstrapper/bootstrapper/Resources.resx.lcl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysSetup/PowerToysSetup.wixproj b/installer/PowerToysSetup/PowerToysSetup.wixproj index 8c5ad9d3d6..f7816d7a41 100644 --- a/installer/PowerToysSetup/PowerToysSetup.wixproj +++ b/installer/PowerToysSetup/PowerToysSetup.wixproj @@ -74,7 +74,8 @@ IF NOT DEFINED IsPipeline ( -call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat" -arch=amd64 -host_arch=amd64 -winsdk=10.0.18362.0 + +call "$([MSBuild]::GetVsInstallRoot())\Common7\Tools\VsDevCmd.bat" -arch=amd64 -host_arch=amd64 -winsdk=10.0.18362.0 SET PTRoot=..\..\..\.. call "..\..\publish.cmd" ) diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 347a135148..4d3d72f80b 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -25,7 +25,7 @@ @@ -386,6 +386,7 @@ + @@ -491,6 +492,7 @@ + @@ -592,7 +594,7 @@ - + @@ -627,6 +629,7 @@ + @@ -637,7 +640,7 @@ - + @@ -883,7 +886,7 @@ - + @@ -901,7 +904,7 @@ - + @@ -912,7 +915,7 @@ - + @@ -924,7 +927,7 @@ - + @@ -936,7 +939,7 @@ - + @@ -948,7 +951,7 @@ - + @@ -960,7 +963,7 @@ - + @@ -971,7 +974,7 @@ - + @@ -982,4 +985,4 @@ - \ No newline at end of file + diff --git a/src/Version.props b/src/Version.props index 5c1c8448e2..6b54dbd99a 100644 --- a/src/Version.props +++ b/src/Version.props @@ -1,6 +1,7 @@ - 0.23.1 + 0.0.1 + Local - \ No newline at end of file + diff --git a/src/action_runner/Resources.resx b/src/action_runner/Resources.resx index d1ca376f1a..1b90cd3d36 100644 --- a/src/action_runner/Resources.resx +++ b/src/action_runner/Resources.resx @@ -64,4 +64,67 @@ PowerToys installation error + + An update to PowerToys is available. + + + PowerToys download started. + + + An update to PowerToys is ready to install. + + + Error: couldn't download PowerToys installer. Visit our GitHub page to update. + + + Update now + + + At next launch + + + Error: please uninstall the previous version of PowerToys manually. + + + An update to PowerToys is available. Visit our GitHub page to update. + + + PowerToys is up to date. + + + Visit + + + More info... + + + Abort + + + Click Snooze to be reminded in: + + + 1 day + + + 5 days + + + Downloading... + + + Download complete + + + PowerToys Update + + + We've detected a previous installation of PowerToys. Would you like to remove it? + + + PowerToys: uninstall previous version? + + + Snooze + \ No newline at end of file diff --git a/src/action_runner/action_runner.cpp b/src/action_runner/action_runner.cpp index acabf8d767..321be922b2 100644 --- a/src/action_runner/action_runner.cpp +++ b/src/action_runner/action_runner.cpp @@ -1,4 +1,6 @@ #define WIN32_LEAN_AND_MEAN +#include "Generated Files/resource.h" + #include #include @@ -16,10 +18,10 @@ #include "../runner/tray_icon.h" #include "../runner/action_runner_utils.h" -#include "Generated Files/resource.h" - extern "C" IMAGE_DOS_HEADER __ImageBase; +auto Strings = updating::notifications::strings::create(); + int uninstall_msi_action() { const auto package_path = updating::get_msi_package_path(); @@ -27,7 +29,7 @@ int uninstall_msi_action() { return 0; } - if (!updating::uninstall_msi_version(package_path)) + if (!updating::uninstall_msi_version(package_path, Strings)) { return -1; } diff --git a/src/action_runner/loc/cs/src/runner/Resources.resx.lcl b/src/action_runner/loc/cs/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..f6d43d2ad5 --- /dev/null +++ b/src/action_runner/loc/cs/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/de/src/runner/Resources.resx.lcl b/src/action_runner/loc/de/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..7424804ed1 --- /dev/null +++ b/src/action_runner/loc/de/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/es/src/runner/Resources.resx.lcl b/src/action_runner/loc/es/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..3ced2ecaff --- /dev/null +++ b/src/action_runner/loc/es/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/fr/src/runner/Resources.resx.lcl b/src/action_runner/loc/fr/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..2674cc5036 --- /dev/null +++ b/src/action_runner/loc/fr/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/hu/src/runner/Resources.resx.lcl b/src/action_runner/loc/hu/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..18f1420373 --- /dev/null +++ b/src/action_runner/loc/hu/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/it/src/runner/Resources.resx.lcl b/src/action_runner/loc/it/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..33b7764875 --- /dev/null +++ b/src/action_runner/loc/it/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/ja/src/runner/Resources.resx.lcl b/src/action_runner/loc/ja/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..f8c5716a14 --- /dev/null +++ b/src/action_runner/loc/ja/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/ko/src/runner/Resources.resx.lcl b/src/action_runner/loc/ko/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..2974248ca9 --- /dev/null +++ b/src/action_runner/loc/ko/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/nl/src/runner/Resources.resx.lcl b/src/action_runner/loc/nl/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..80862face2 --- /dev/null +++ b/src/action_runner/loc/nl/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/pl/src/runner/Resources.resx.lcl b/src/action_runner/loc/pl/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..d7c06f208b --- /dev/null +++ b/src/action_runner/loc/pl/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/pt-BR/src/runner/Resources.resx.lcl b/src/action_runner/loc/pt-BR/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..ec55d333e5 --- /dev/null +++ b/src/action_runner/loc/pt-BR/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/pt-PT/src/runner/Resources.resx.lcl b/src/action_runner/loc/pt-PT/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..22bf873d32 --- /dev/null +++ b/src/action_runner/loc/pt-PT/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/ru/src/runner/Resources.resx.lcl b/src/action_runner/loc/ru/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..9c5febc6f5 --- /dev/null +++ b/src/action_runner/loc/ru/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/sv/src/runner/Resources.resx.lcl b/src/action_runner/loc/sv/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..bec5143a50 --- /dev/null +++ b/src/action_runner/loc/sv/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/tr/src/runner/Resources.resx.lcl b/src/action_runner/loc/tr/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..4dc1589b9d --- /dev/null +++ b/src/action_runner/loc/tr/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/zh-Hans/src/runner/Resources.resx.lcl b/src/action_runner/loc/zh-Hans/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..6443430967 --- /dev/null +++ b/src/action_runner/loc/zh-Hans/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/action_runner/loc/zh-Hant/src/runner/Resources.resx.lcl b/src/action_runner/loc/zh-Hant/src/runner/Resources.resx.lcl new file mode 100644 index 0000000000..62e50f25ad --- /dev/null +++ b/src/action_runner/loc/zh-Hant/src/runner/Resources.resx.lcl @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/common/common.cpp b/src/common/common.cpp index 8d9d251e72..2c7d3e919d 100644 --- a/src/common/common.cpp +++ b/src/common/common.cpp @@ -85,22 +85,22 @@ std::optional get_last_error_message(const DWORD dw) return message; } -void show_last_error_message(LPCWSTR lpszFunction, DWORD dw, LPCWSTR errorTitle) +void show_last_error_message(LPCWSTR functionName, DWORD dw, LPCWSTR errorTitle) { const auto system_message = get_last_error_message(dw); if (!system_message.has_value()) { return; } - LPWSTR lpDisplayBuf = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, (system_message->size() + lstrlenW(lpszFunction) + 40) * sizeof(WCHAR)); + LPWSTR lpDisplayBuf = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, (system_message->size() + lstrlenW(functionName) + 40) * sizeof(WCHAR)); if (lpDisplayBuf != NULL) { StringCchPrintfW(lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(WCHAR), - localized_strings::LAST_ERROR_FORMAT_STRING, - lpszFunction, - dw, - system_message->c_str()); + L"%s: %s (%d)", + functionName, + system_message->c_str(), + dw); MessageBoxW(NULL, (LPCTSTR)lpDisplayBuf, errorTitle, MB_OK | MB_ICONERROR); LocalFree(lpDisplayBuf); } diff --git a/src/common/common.h b/src/common/common.h index e826702a7b..ed3db9e0a4 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -7,12 +7,6 @@ #include -namespace localized_strings -{ - const wchar_t LAST_ERROR_FORMAT_STRING[] = L"%s failed with error %d: %s"; - const wchar_t LAST_ERROR_TITLE_STRING[] = L"Error"; -} - // Gets position of given window. std::optional get_window_pos(HWND hwnd); @@ -23,7 +17,7 @@ bool is_system_window(HWND hwnd, const char* class_name); int run_message_loop(const bool until_idle = false, const std::optional timeout_seconds = {}); std::optional get_last_error_message(const DWORD dw); -void show_last_error_message(LPCWSTR lpszFunction, DWORD dw, LPCWSTR errorTitle = localized_strings::LAST_ERROR_TITLE_STRING); +void show_last_error_message(LPCWSTR lpszFunction, DWORD dw, LPCWSTR errorTitle); enum WindowState { diff --git a/src/common/notifications.cpp b/src/common/notifications.cpp index f87064b4cd..bb48d76feb 100644 --- a/src/common/notifications.cpp +++ b/src/common/notifications.cpp @@ -33,8 +33,7 @@ using winrt::Windows::UI::Notifications::ToastNotificationManager; namespace fs = std::filesystem; -// This namespace contains strings that SHOULD NOT be localized -namespace +namespace // Strings in this namespace should not be localized { constexpr std::wstring_view TASK_NAME = L"PowerToysBackgroundNotificationsHandler"; constexpr std::wstring_view TASK_ENTRYPOINT = L"PowerToysNotifications.BackgroundHandler"; @@ -45,11 +44,6 @@ namespace constexpr std::wstring_view DEFAULT_TOAST_GROUP = L"PowerToysToastTag"; } -namespace localized_strings -{ - constexpr std::wstring_view SNOOZE_BUTTON = L"Snooze"; -} - static DWORD loop_thread_id() { static const DWORD thread_id = GetCurrentThreadId(); @@ -365,7 +359,7 @@ void notifications::show_toast_with_activations(std::wstring message, toast_xml += '"'; } toast_xml += LR"( content=")"; - toast_xml += localized_strings::SNOOZE_BUTTON; + toast_xml += b.snooze_button_title; toast_xml += LR"(" />)"; } }, actions[i]); diff --git a/src/common/notifications.h b/src/common/notifications.h index ac52539b4f..01a0a986e0 100644 --- a/src/common/notifications.h +++ b/src/common/notifications.h @@ -28,6 +28,7 @@ namespace notifications { std::wstring snooze_title; std::vector durations; + std::wstring snooze_button_title; }; struct link_button @@ -45,7 +46,7 @@ namespace notifications struct progress_bar_params { - std::wstring_view progress_title; + std::wstring progress_title; float progress = 0.f; }; diff --git a/src/common/updating/notifications.cpp b/src/common/updating/notifications.cpp index 8984df265f..01e523e657 100644 --- a/src/common/updating/notifications.cpp +++ b/src/common/updating/notifications.cpp @@ -9,39 +9,10 @@ #include "VersionHelper.h" #include "version.h" -namespace -{ - const wchar_t TOAST_TITLE[] = L"PowerToys Update"; -} - -namespace localized_strings -{ - const wchar_t GITHUB_NEW_VERSION_AVAILABLE[] = L"An update to PowerToys is available.\n"; - const wchar_t GITHUB_NEW_VERSION_DOWNLOAD_STARTED[] = L"PowerToys download started.\n"; - const wchar_t GITHUB_NEW_VERSION_READY_TO_INSTALL[] = L"An update to PowerToys is ready to install.\n"; - const wchar_t GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR[] = L"Error: couldn't download PowerToys installer. Visit our GitHub page to update.\n"; - const wchar_t GITHUB_NEW_VERSION_UPDATE_NOW[] = L"Update now"; - const wchar_t GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART[] = L"At next launch"; - - const wchar_t UNINSTALLATION_UNKNOWN_ERROR[] = L"Error: please uninstall the previous version of PowerToys manually."; - - const wchar_t GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT[] = L"An update to PowerToys is available. Visit our GitHub page to update.\n"; - const wchar_t GITHUB_NEW_VERSION_UNAVAILABLE[] = L"PowerToys is up to date.\n"; - const wchar_t GITHUB_NEW_VERSION_VISIT[] = L"Visit"; - const wchar_t GITHUB_NEW_VERSION_MORE_INFO[] = L"More info..."; - const wchar_t GITHUB_NEW_VERSION_ABORT[] = L"Abort"; - const wchar_t GITHUB_NEW_VERSION_SNOOZE_TITLE[] = L"Click Snooze to be reminded in:"; - const wchar_t GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D[] = L"1 day"; - const wchar_t GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D[] = L"5 days"; - const wchar_t DOWNLOAD_IN_PROGRESS[] = L"Downloading..."; - const wchar_t DOWNLOAD_COMPLETE[] = L"Download complete"; -} - namespace updating { namespace notifications { - using namespace localized_strings; using namespace ::notifications; std::wstring current_version_to_next_version(const updating::new_version_download_info& info) { @@ -51,108 +22,123 @@ namespace updating return current_version_to_next_version; } - void show_unavailable() + void show_unavailable(const notifications::strings& strings) { remove_toasts(UPDATING_PROCESS_TOAST_TAG); toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false }; - std::wstring contents = GITHUB_NEW_VERSION_UNAVAILABLE; - show_toast(std::move(contents), TOAST_TITLE, std::move(toast_params)); + std::wstring contents = strings.GITHUB_NEW_VERSION_UNAVAILABLE; + show_toast(std::move(contents), strings.TOAST_TITLE, std::move(toast_params)); } - void show_available(const updating::new_version_download_info& info) + void show_available(const updating::new_version_download_info& info, const notifications::strings& strings) { remove_toasts(UPDATING_PROCESS_TOAST_TAG); toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false }; - std::wstring contents = GITHUB_NEW_VERSION_AVAILABLE; + std::wstring contents = strings.GITHUB_NEW_VERSION_AVAILABLE; + contents += L'\n'; contents += current_version_to_next_version(info); show_toast_with_activations(std::move(contents), - TOAST_TITLE, + strings.TOAST_TITLE, {}, - { link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://download_and_install_update/" }, link_button{ GITHUB_NEW_VERSION_MORE_INFO, info.release_page_uri.ToString().c_str() } }, + { link_button{ strings.GITHUB_NEW_VERSION_UPDATE_NOW, + L"powertoys://download_and_install_update/" }, + link_button{ strings.GITHUB_NEW_VERSION_MORE_INFO, + info.release_page_uri.ToString().c_str() } }, std::move(toast_params)); } - void show_download_start(const updating::new_version_download_info& info) + void show_download_start(const updating::new_version_download_info& info, const notifications::strings& strings) { remove_toasts(UPDATING_PROCESS_TOAST_TAG); progress_bar_params progress_bar_params; std::wstring progress_title{ info.version_string }; progress_title += L' '; - progress_title += localized_strings::DOWNLOAD_IN_PROGRESS; + progress_title += strings.DOWNLOAD_IN_PROGRESS; progress_bar_params.progress_title = progress_title; progress_bar_params.progress = .0f; toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false, std::move(progress_bar_params) }; - show_toast_with_activations(localized_strings::GITHUB_NEW_VERSION_DOWNLOAD_STARTED, - TOAST_TITLE, + show_toast_with_activations(strings.GITHUB_NEW_VERSION_DOWNLOAD_STARTED, + strings.TOAST_TITLE, {}, {}, std::move(toast_params)); } - void show_visit_github(const updating::new_version_download_info& info) + void show_visit_github(const updating::new_version_download_info& info, const notifications::strings& strings) { remove_toasts(UPDATING_PROCESS_TOAST_TAG); toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false }; - std::wstring contents = GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT; + std::wstring contents = strings.GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT; + contents += L'\n'; contents += current_version_to_next_version(info); show_toast_with_activations(std::move(contents), - TOAST_TITLE, + strings.TOAST_TITLE, {}, - { link_button{ GITHUB_NEW_VERSION_VISIT, info.release_page_uri.ToString().c_str() } }, + { link_button{ strings.GITHUB_NEW_VERSION_VISIT, + info.release_page_uri.ToString().c_str() } }, std::move(toast_params)); } - void show_install_error(const updating::new_version_download_info& info) + void show_install_error(const updating::new_version_download_info& info, const notifications::strings& strings) { remove_toasts(UPDATING_PROCESS_TOAST_TAG); toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false }; - std::wstring contents = GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR; + std::wstring contents = strings.GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR; + contents += L'\n'; contents += current_version_to_next_version(info); show_toast_with_activations(std::move(contents), - TOAST_TITLE, + strings.TOAST_TITLE, {}, - { link_button{ GITHUB_NEW_VERSION_VISIT, info.release_page_uri.ToString().c_str() } }, + { link_button{ strings.GITHUB_NEW_VERSION_VISIT, info.release_page_uri.ToString().c_str() } }, std::move(toast_params)); } - void show_version_ready(const updating::new_version_download_info& info) + void show_version_ready(const updating::new_version_download_info& info, const notifications::strings& strings) { remove_toasts(UPDATING_PROCESS_TOAST_TAG); toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false }; - std::wstring new_version_ready{ GITHUB_NEW_VERSION_READY_TO_INSTALL }; + std::wstring new_version_ready{ strings.GITHUB_NEW_VERSION_READY_TO_INSTALL }; + new_version_ready += L'\n'; new_version_ready += current_version_to_next_version(info); show_toast_with_activations(std::move(new_version_ready), - TOAST_TITLE, + strings.TOAST_TITLE, {}, - { link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://update_now/" + info.installer_filename }, - link_button{ GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART, L"powertoys://schedule_update/" + info.installer_filename }, - snooze_button{ GITHUB_NEW_VERSION_SNOOZE_TITLE, { { GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D, 24 * 60 }, { GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D, 120 * 60 } } } }, + { link_button{ strings.GITHUB_NEW_VERSION_UPDATE_NOW, + L"powertoys://update_now/" + info.installer_filename }, + link_button{ strings.GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART, + L"powertoys://schedule_update/" + info.installer_filename }, + snooze_button{ + strings.GITHUB_NEW_VERSION_SNOOZE_TITLE, + { { strings.GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D, 24 * 60 }, + { strings.GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D, 120 * 60 } }, + strings.SNOOZE_BUTTON + } }, std::move(toast_params)); } - void show_uninstallation_error() + void show_uninstallation_error(const notifications::strings& strings) { remove_toasts(UPDATING_PROCESS_TOAST_TAG); - show_toast(localized_strings::UNINSTALLATION_UNKNOWN_ERROR, TOAST_TITLE); + show_toast(strings.UNINSTALLATION_UNKNOWN_ERROR, strings.TOAST_TITLE); } - void update_download_progress(const updating::new_version_download_info& info, float progress) + void update_download_progress(const updating::new_version_download_info& info, float progress, const notifications::strings& strings) { progress_bar_params progress_bar_params; std::wstring progress_title{ info.version_string }; progress_title += L' '; - progress_title += progress < 1 ? localized_strings::DOWNLOAD_IN_PROGRESS : localized_strings::DOWNLOAD_COMPLETE; + progress_title += progress < 1 ? strings.DOWNLOAD_IN_PROGRESS : strings.DOWNLOAD_COMPLETE; progress_bar_params.progress_title = progress_title; progress_bar_params.progress = progress; update_toast_progress_bar(UPDATING_PROCESS_TOAST_TAG, progress_bar_params); diff --git a/src/common/updating/notifications.h b/src/common/updating/notifications.h index 30f33f0470..0bd791620f 100644 --- a/src/common/updating/notifications.h +++ b/src/common/updating/notifications.h @@ -6,14 +6,66 @@ namespace updating namespace notifications { - void show_unavailable(); - void show_available(const updating::new_version_download_info& info); - void show_download_start(const updating::new_version_download_info& info); - void show_visit_github(const updating::new_version_download_info& info); - void show_install_error(const updating::new_version_download_info& info); - void show_version_ready(const updating::new_version_download_info& info); - void show_uninstallation_error(); + struct strings + { + std::wstring GITHUB_NEW_VERSION_AVAILABLE; + std::wstring GITHUB_NEW_VERSION_DOWNLOAD_STARTED; + std::wstring GITHUB_NEW_VERSION_READY_TO_INSTALL; + std::wstring GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR; + std::wstring GITHUB_NEW_VERSION_UPDATE_NOW; + std::wstring GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART; + std::wstring UNINSTALLATION_UNKNOWN_ERROR; + std::wstring GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT; + std::wstring GITHUB_NEW_VERSION_UNAVAILABLE; + std::wstring GITHUB_NEW_VERSION_VISIT; + std::wstring GITHUB_NEW_VERSION_MORE_INFO; + std::wstring GITHUB_NEW_VERSION_ABORT; + std::wstring GITHUB_NEW_VERSION_SNOOZE_TITLE; + std::wstring SNOOZE_BUTTON; + std::wstring GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D; + std::wstring GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D; + std::wstring DOWNLOAD_IN_PROGRESS; + std::wstring DOWNLOAD_COMPLETE; + std::wstring TOAST_TITLE; + std::wstring OFFER_UNINSTALL_MSI; + std::wstring OFFER_UNINSTALL_MSI_TITLE; + template + static strings create() + { + return strings{ + .GITHUB_NEW_VERSION_AVAILABLE = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_AVAILABLE), + .GITHUB_NEW_VERSION_DOWNLOAD_STARTED = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_DOWNLOAD_STARTED), + .GITHUB_NEW_VERSION_READY_TO_INSTALL = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_READY_TO_INSTALL), + .GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR), + .GITHUB_NEW_VERSION_UPDATE_NOW = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_UPDATE_NOW), + .GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART), + .UNINSTALLATION_UNKNOWN_ERROR = GET_RESOURCE_STRING(IDS_UNINSTALLATION_UNKNOWN_ERROR), + .GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT), + .GITHUB_NEW_VERSION_UNAVAILABLE = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_UNAVAILABLE), + .GITHUB_NEW_VERSION_VISIT = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_VISIT), + .GITHUB_NEW_VERSION_MORE_INFO = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_MORE_INFO), + .GITHUB_NEW_VERSION_ABORT = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_ABORT), + .GITHUB_NEW_VERSION_SNOOZE_TITLE = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_SNOOZE_TITLE), + .SNOOZE_BUTTON = GET_RESOURCE_STRING(IDS_SNOOZE_BUTTON), + .GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D), + .GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D), + .DOWNLOAD_IN_PROGRESS = GET_RESOURCE_STRING(IDS_DOWNLOAD_IN_PROGRESS), + .DOWNLOAD_COMPLETE = GET_RESOURCE_STRING(IDS_DOWNLOAD_COMPLETE), + .TOAST_TITLE = GET_RESOURCE_STRING(IDS_TOAST_TITLE), + .OFFER_UNINSTALL_MSI = GET_RESOURCE_STRING(IDS_OFFER_UNINSTALL_MSI), + .OFFER_UNINSTALL_MSI_TITLE = GET_RESOURCE_STRING(IDS_OFFER_UNINSTALL_MSI_TITLE) + }; + } + }; - void update_download_progress(const updating::new_version_download_info& info, float progress); + void show_unavailable(const notifications::strings& strings); + void show_available(const updating::new_version_download_info& info, const strings&); + void show_download_start(const updating::new_version_download_info& info, const strings&); + void show_visit_github(const updating::new_version_download_info& info, const strings&); + void show_install_error(const updating::new_version_download_info& info, const strings&); + void show_version_ready(const updating::new_version_download_info& info, const strings&); + void show_uninstallation_error(const notifications::strings& strings); + + void update_download_progress(const updating::new_version_download_info& info, float progress, const notifications::strings& strings); } } \ No newline at end of file diff --git a/src/common/updating/updating.cpp b/src/common/updating/updating.cpp index c2f0e6e536..0160d4b0fc 100644 --- a/src/common/updating/updating.cpp +++ b/src/common/updating/updating.cpp @@ -19,7 +19,7 @@ #include "VersionHelper.h" #include -namespace +namespace // Strings in this namespace should not be localized { const wchar_t POWER_TOYS_UPGRADE_CODE[] = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}"; const wchar_t POWERTOYS_EXE_COMPONENT[] = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}"; @@ -32,12 +32,6 @@ namespace const wchar_t TOAST_TITLE[] = L"PowerToys"; } -namespace localized_strings -{ - const wchar_t OFFER_UNINSTALL_MSI[] = L"We've detected a previous installation of PowerToys. Would you like to remove it?"; - const wchar_t OFFER_UNINSTALL_MSI_TITLE[] = L"PowerToys: uninstall previous version?"; -} - namespace updating { std::wstring get_msi_package_path() @@ -73,13 +67,18 @@ namespace updating return package_path; } - bool offer_msi_uninstallation() + bool offer_msi_uninstallation(const notifications::strings& strings) { - const auto selection = SHMessageBoxCheckW(nullptr, localized_strings::OFFER_UNINSTALL_MSI, localized_strings::OFFER_UNINSTALL_MSI_TITLE, MB_ICONQUESTION | MB_YESNO, IDNO, DONT_SHOW_AGAIN_RECORD_REGISTRY_PATH); + const auto selection = SHMessageBoxCheckW(nullptr, + strings.OFFER_UNINSTALL_MSI.c_str(), + strings.OFFER_UNINSTALL_MSI_TITLE.c_str(), + MB_ICONQUESTION | MB_YESNO, + IDNO, + DONT_SHOW_AGAIN_RECORD_REGISTRY_PATH); return selection == IDYES; } - bool uninstall_msi_version(const std::wstring& package_path) + bool uninstall_msi_version(const std::wstring& package_path, const notifications::strings& strings) { const auto uninstall_result = MsiInstallProductW(package_path.c_str(), L"REMOVE=ALL"); if (ERROR_SUCCESS == uninstall_result) @@ -94,7 +93,7 @@ namespace updating } catch (...) { - updating::notifications::show_uninstallation_error(); + updating::notifications::show_uninstallation_error(strings); } } return false; @@ -202,7 +201,7 @@ namespace updating return installer_download_dst; } - std::future try_autoupdate(const bool download_updates_automatically) + std::future try_autoupdate(const bool download_updates_automatically, const notifications::strings& strings) { const auto new_version = co_await get_new_github_version_info_async(); if (!new_version) @@ -230,32 +229,32 @@ namespace updating } if (!download_success) { - updating::notifications::show_install_error(new_version.value()); + updating::notifications::show_install_error(new_version.value(), strings); co_return; } - updating::notifications::show_version_ready(new_version.value()); + updating::notifications::show_version_ready(new_version.value(), strings); } else { - updating::notifications::show_visit_github(new_version.value()); + updating::notifications::show_visit_github(new_version.value(), strings); } } - std::future check_new_version_available() + std::future check_new_version_available(const notifications::strings& strings) { const auto new_version = co_await get_new_github_version_info_async(); if (!new_version) { - updating::notifications::show_unavailable(); + updating::notifications::show_unavailable(strings); co_return VersionHelper{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION }.toWstring(); } - updating::notifications::show_available(new_version.value()); + updating::notifications::show_available(new_version.value(), strings); co_return new_version->version_string; } - std::future download_update() + std::future download_update(const notifications::strings& strings) { const auto new_version = co_await get_new_github_version_info_async(); if (!new_version) @@ -264,12 +263,12 @@ namespace updating } auto installer_download_dst = create_download_path() / new_version->installer_filename; - updating::notifications::show_download_start(new_version.value()); + updating::notifications::show_download_start(new_version.value(), strings); try { auto progressUpdateHandle = [&](float progress) { - updating::notifications::update_download_progress(new_version.value(), progress); + updating::notifications::update_download_progress(new_version.value(), progress, strings); }; http::HttpClient client; @@ -277,7 +276,7 @@ namespace updating } catch (...) { - updating::notifications::show_install_error(new_version.value()); + updating::notifications::show_install_error(new_version.value(), strings); co_return L""; } diff --git a/src/common/updating/updating.h b/src/common/updating/updating.h index 597a866b16..ea993f6d60 100644 --- a/src/common/updating/updating.h +++ b/src/common/updating/updating.h @@ -6,13 +6,14 @@ #include #include +#include "notifications.h" #include "../VersionHelper.h" namespace updating { std::wstring get_msi_package_path(); - bool uninstall_msi_version(const std::wstring& package_path); - bool offer_msi_uninstallation(); + bool uninstall_msi_version(const std::wstring& package_path, const notifications::strings&); + bool offer_msi_uninstallation(const notifications::strings&); std::optional get_msi_package_installed_path(); std::optional get_installed_powertoys_version(); @@ -27,11 +28,11 @@ namespace updating }; std::future> get_new_github_version_info_async(); - std::future try_autoupdate(const bool download_updates_automatically); + std::future try_autoupdate(const bool download_updates_automatically, const notifications::strings&); std::filesystem::path get_pending_updates_path(); - std::future check_new_version_available(); - std::future download_update(); + std::future check_new_version_available(const notifications::strings&); + std::future download_update(const notifications::strings&); // non-localized constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN = L"powertoyssetup"; diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/ISettingsPath.cs b/src/core/Microsoft.PowerToys.Settings.UI.Library/ISettingsPath.cs new file mode 100644 index 0000000000..072058e4bc --- /dev/null +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/ISettingsPath.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.Library +{ + public interface ISettingsPath + { + bool SettingsFolderExists(string powertoy); + + void CreateSettingsFolder(string powertoy); + + void DeleteSettings(string powertoy = ""); + + string GetSettingsPath(string powertoy, string fileName); + } +} diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/Microsoft.PowerToys.Settings.UI.Library.csproj b/src/core/Microsoft.PowerToys.Settings.UI.Library/Microsoft.PowerToys.Settings.UI.Library.csproj index 4c94051072..dc4cf10d0b 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Library/Microsoft.PowerToys.Settings.UI.Library.csproj +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/Microsoft.PowerToys.Settings.UI.Library.csproj @@ -41,6 +41,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + 3.3.0 diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/SettingPath.cs b/src/core/Microsoft.PowerToys.Settings.UI.Library/SettingPath.cs new file mode 100644 index 0000000000..65a1ea1244 --- /dev/null +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/SettingPath.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO.Abstractions; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class SettingPath : ISettingsPath + { + private const string DefaultFileName = "settings.json"; + + private readonly IDirectory _directory; + + private readonly IPath _path; + + public SettingPath(IDirectory directory, IPath path) + { + _directory = directory ?? throw new ArgumentNullException(nameof(directory)); + _path = path ?? throw new ArgumentNullException(nameof(path)); + } + + public bool SettingsFolderExists(string powertoy) + { + return _directory.Exists(System.IO.Path.Combine(LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}")); + } + + public void CreateSettingsFolder(string powertoy) + { + _directory.CreateDirectory(System.IO.Path.Combine(LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}")); + } + + public void DeleteSettings(string powertoy = "") + { + _directory.Delete(System.IO.Path.Combine(LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}")); + } + + private static string LocalApplicationDataFolder() + { + return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + } + + /// + /// Get path to the json settings file. + /// + /// string path. + public string GetSettingsPath(string powertoy, string fileName = DefaultFileName) + { + if (string.IsNullOrWhiteSpace(powertoy)) + { + return _path.Combine( + LocalApplicationDataFolder(), + $"Microsoft\\PowerToys\\{fileName}"); + } + + return _path.Combine( + LocalApplicationDataFolder(), + $"Microsoft\\PowerToys\\{powertoy}\\{fileName}"); + } + } +} diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/SettingsUtils.cs b/src/core/Microsoft.PowerToys.Settings.UI.Library/SettingsUtils.cs index 13a9b9ba69..e7fc1dec69 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Library/SettingsUtils.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/SettingsUtils.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.IO.Abstractions; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; using Microsoft.PowerToys.Settings.UI.Library.Utilities; @@ -15,49 +16,34 @@ namespace Microsoft.PowerToys.Settings.UI.Library { private const string DefaultFileName = "settings.json"; private const string DefaultModuleName = ""; - private IIOProvider _ioProvider; + private readonly IFile _file; + private readonly ISettingsPath _settingsPath; - public SettingsUtils(IIOProvider ioProvider) + public SettingsUtils() + : this(new FileSystem()) { - _ioProvider = ioProvider ?? throw new ArgumentNullException(nameof(ioProvider)); } - private bool SettingsFolderExists(string powertoy) + public SettingsUtils(IFileSystem fileSystem) + : this(fileSystem?.File, new SettingPath(fileSystem?.Directory, fileSystem?.Path)) { - return _ioProvider.DirectoryExists(System.IO.Path.Combine(LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}")); } - private void CreateSettingsFolder(string powertoy) + public SettingsUtils(IFile file, ISettingsPath settingPath) { - _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}")); - } - - /// - /// Get path to the json settings file. - /// - /// string path. - public static string GetSettingsPath(string powertoy, string fileName = DefaultFileName) - { - if (string.IsNullOrWhiteSpace(powertoy)) - { - return System.IO.Path.Combine( - LocalApplicationDataFolder(), - $"Microsoft\\PowerToys\\{fileName}"); - } - - return System.IO.Path.Combine( - LocalApplicationDataFolder(), - $"Microsoft\\PowerToys\\{powertoy}\\{fileName}"); + _file = file ?? throw new ArgumentNullException(nameof(file)); + _settingsPath = settingPath; } public bool SettingsExists(string powertoy = DefaultModuleName, string fileName = DefaultFileName) { - return _ioProvider.FileExists(GetSettingsPath(powertoy, fileName)); + var settingsPath = _settingsPath.GetSettingsPath(powertoy, fileName); + return _file.Exists(settingsPath); + } + + public void DeleteSettings(string powertoy = "") + { + _settingsPath.DeleteSettings(powertoy); } /// @@ -106,7 +92,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library // 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(powertoyFolderName, fileName)).Trim('\0'); + var jsonSettingsString = _file.ReadAllText(_settingsPath.GetSettingsPath(powertoyFolderName, fileName)).Trim('\0'); return JsonSerializer.Deserialize(jsonSettingsString); } @@ -118,12 +104,12 @@ namespace Microsoft.PowerToys.Settings.UI.Library { if (jsonSettings != null) { - if (!SettingsFolderExists(powertoy)) + if (!_settingsPath.SettingsFolderExists(powertoy)) { - CreateSettingsFolder(powertoy); + _settingsPath.CreateSettingsFolder(powertoy); } - _ioProvider.WriteAllText(GetSettingsPath(powertoy, fileName), jsonSettings); + _file.WriteAllText(_settingsPath.GetSettingsPath(powertoy, fileName), jsonSettings); } } catch (Exception e) @@ -137,10 +123,5 @@ namespace Microsoft.PowerToys.Settings.UI.Library #endif } } - - private static string LocalApplicationDataFolder() - { - return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/Utilities/Helper.cs b/src/core/Microsoft.PowerToys.Settings.UI.Library/Utilities/Helper.cs index 8cce1f2a68..22b2964592 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Library/Utilities/Helper.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/Utilities/Helper.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.IO; +using System.IO.Abstractions; using System.Linq; using System.Runtime.InteropServices; using Microsoft.PowerToys.Settings.UI.Library.CustomAction; @@ -13,6 +14,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Utilities { public static class Helper { + public static readonly IFileSystem FileSystem = new FileSystem(); + public static bool AllowRunnerToForeground() { var result = false; @@ -47,22 +50,20 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Utilities return sendCustomAction.ToJsonString(); } - public static FileSystemWatcher GetFileWatcher(string moduleName, string fileName, Action onChangedCallback) + public static IFileSystemWatcher GetFileWatcher(string moduleName, string fileName, Action onChangedCallback) { - var path = Path.Combine(LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{moduleName}"); + var path = FileSystem.Path.Combine(LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{moduleName}"); - if (!Directory.Exists(path)) + if (!FileSystem.Directory.Exists(path)) { - Directory.CreateDirectory(path); + FileSystem.Directory.CreateDirectory(path); } - var watcher = new FileSystemWatcher - { - Path = path, - Filter = fileName, - NotifyFilter = NotifyFilters.LastWrite, - EnableRaisingEvents = true, - }; + var watcher = FileSystem.FileSystemWatcher.CreateNew(); + watcher.Path = path; + watcher.Filter = fileName; + watcher.NotifyFilter = NotifyFilters.LastWrite; + watcher.EnableRaisingEvents = true; watcher.Changed += (o, e) => onChangedCallback(); diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/Utilities/Logger.cs b/src/core/Microsoft.PowerToys.Settings.UI.Library/Utilities/Logger.cs index 7d98018af7..fbac2c88fb 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Library/Utilities/Logger.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/Utilities/Logger.cs @@ -5,12 +5,16 @@ using System; using System.Diagnostics; using System.Globalization; -using System.IO; +using System.IO.Abstractions; namespace Microsoft.PowerToys.Settings.UI.Library.Utilities { public static class Logger { + private static readonly IFileSystem FileSystem = new FileSystem(); + private static readonly IPath Path = FileSystem.Path; + private static readonly IDirectory Directory = FileSystem.Directory; + private static readonly string ApplicationLogPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\PowerToys\\Settings Logs"); static Logger() @@ -20,6 +24,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Utilities Directory.CreateDirectory(ApplicationLogPath); } + // Using InvariantCulture since this is used for a log file name var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/Utilities/SystemIOProvider.cs b/src/core/Microsoft.PowerToys.Settings.UI.Library/Utilities/SystemIOProvider.cs index c650c5957e..ada9b15fbc 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Library/Utilities/SystemIOProvider.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/Utilities/SystemIOProvider.cs @@ -2,41 +2,61 @@ // 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; +using System.IO.Abstractions; namespace Microsoft.PowerToys.Settings.UI.Library.Utilities { public class SystemIOProvider : IIOProvider { + private readonly IDirectory _directory; + private readonly IFile _file; + + public SystemIOProvider() + : this(new FileSystem()) + { + } + + public SystemIOProvider(IFileSystem fileSystem) + : this(fileSystem?.Directory, fileSystem?.File) + { + } + + private SystemIOProvider(IDirectory directory, IFile file) + { + _directory = directory ?? throw new ArgumentNullException(nameof(directory)); + _file = file ?? throw new ArgumentNullException(nameof(file)); + } + public bool CreateDirectory(string path) { - var directioryInfo = Directory.CreateDirectory(path); + var directioryInfo = _directory.CreateDirectory(path); return directioryInfo != null; } public void DeleteDirectory(string path) { - Directory.Delete(path, recursive: true); + _directory.Delete(path, recursive: true); } public bool DirectoryExists(string path) { - return Directory.Exists(path); + return _directory.Exists(path); } public bool FileExists(string path) { - return File.Exists(path); + return _file.Exists(path); } public string ReadAllText(string path) { - return File.ReadAllText(path); + return _file.ReadAllText(path); } public void WriteAllText(string path, string content) { - File.WriteAllText(path, content); + _file.WriteAllText(path, content); } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Runner/MainWindow.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI.Runner/MainWindow.xaml.cs index 04ac56367d..d2353b0ced 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Runner/MainWindow.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Runner/MainWindow.xaml.cs @@ -44,21 +44,21 @@ namespace Microsoft.PowerToys.Settings.UI.Runner if (shellPage != null) { // send IPC Message - shellPage.SetDefaultSndMessageCallback(msg => + ShellPage.SetDefaultSndMessageCallback(msg => { // IPC Manager is null when launching runner directly Program.GetTwoWayIPCManager()?.Send(msg); }); // send IPC Message - shellPage.SetRestartAdminSndMessageCallback(msg => + ShellPage.SetRestartAdminSndMessageCallback(msg => { Program.GetTwoWayIPCManager().Send(msg); System.Windows.Application.Current.Shutdown(); // close application }); // send IPC Message - shellPage.SetCheckForUpdatesMessageCallback(msg => + ShellPage.SetCheckForUpdatesMessageCallback(msg => { Program.GetTwoWayIPCManager().Send(msg); }); @@ -83,8 +83,8 @@ namespace Microsoft.PowerToys.Settings.UI.Runner } }; - shellPage.SetElevationStatus(Program.IsElevated); - shellPage.SetIsUserAnAdmin(Program.IsUserAnAdmin); + ShellPage.SetElevationStatus(Program.IsElevated); + ShellPage.SetIsUserAnAdmin(Program.IsUserAnAdmin); shellPage.Refresh(); } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs index 28ed4f04bc..36ec7b6ffa 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs @@ -1,13 +1,11 @@ using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; -using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Moq; using System; -using System.Collections.Generic; using System.Globalization; +using System.IO.Abstractions; using System.Linq.Expressions; -using System.Text; namespace Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility { @@ -15,6 +13,9 @@ namespace Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility { public const string RootPathStubFiles = "..\\..\\..\\..\\src\\core\\Microsoft.PowerToys.Settings.UI.UnitTests\\BackwardsCompatibility\\TestFiles\\{0}\\Microsoft\\PowerToys\\{1}\\{2}"; + // Using Ordinal since this is used internally for a path + private static readonly Expression> SettingsFilterExpression = s => s == null || s.Contains("Microsoft\\PowerToys\\settings.json", StringComparison.Ordinal); + internal class MockSettingsRepository : ISettingsRepository where T : ISettingsConfig, new() { T _settingsConfig; @@ -43,44 +44,56 @@ namespace Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility } - public static MockGetModuleIOProvider(string version, string module, string fileName) + public static MockGetModuleIOProvider(string version, string module, string fileName) { - var stubSettingsPath = string.Format(CultureInfo.InvariantCulture, BackCompatTestProperties.RootPathStubFiles, version, module, fileName); - Expression> filterExpression = (string s) => s.Contains(module, StringComparison.Ordinal); - var mockIOProvider = IIOProviderMocks.GetMockIOReadWithStubFile(stubSettingsPath, filterExpression); - return mockIOProvider; + var stubSettingsPath = StubSettingsPath(version, module, fileName); + Expression> filterExpression = ModuleFilterExpression(module); + return IIOProviderMocks.GetMockIOReadWithStubFile(stubSettingsPath, filterExpression); } - public static void VerifyModuleIOProviderWasRead(Mock provider, string module, int expectedCallCount) + public static string StubGeneralSettingsPath(string version) + { + return StubSettingsPath(version, string.Empty, "settings.json"); + } + + public static string StubSettingsPath(string version, string module, string fileName) + { + return string.Format(CultureInfo.InvariantCulture, BackCompatTestProperties.RootPathStubFiles, version, module, fileName); + } + + public static void VerifyModuleIOProviderWasRead(Mock provider, string module, int expectedCallCount) { if(provider == null) { throw new ArgumentNullException(nameof(provider)); } - Expression> filterExpression = (string s) => s.Contains(module, StringComparison.Ordinal); + Expression> filterExpression = ModuleFilterExpression(module); IIOProviderMocks.VerifyIOReadWithStubFile(provider, filterExpression, expectedCallCount); } - public static Mock GetGeneralSettingsIOProvider(string version) + private static Expression> ModuleFilterExpression(string module) { - var stubGeneralSettingsPath = string.Format(CultureInfo.InvariantCulture, BackCompatTestProperties.RootPathStubFiles, version, string.Empty, "settings.json"); - Expression> filterExpression = (string s) => s.Contains("Microsoft\\PowerToys\\settings.json", StringComparison.Ordinal); - var mockGeneralIOProvider = IIOProviderMocks.GetMockIOReadWithStubFile(stubGeneralSettingsPath, filterExpression); - return mockGeneralIOProvider; + // Using Ordinal since this is used internally for a path + return s => s == null || s.Contains(module, StringComparison.Ordinal); } - public static void VerifyGeneralSettingsIOProviderWasRead(Mock provider, int expectedCallCount) + public static Mock GetGeneralSettingsIOProvider(string version) + { + var stubGeneralSettingsPath = StubGeneralSettingsPath(version); + return IIOProviderMocks.GetMockIOReadWithStubFile(stubGeneralSettingsPath, SettingsFilterExpression); + } + + public static void VerifyGeneralSettingsIOProviderWasRead(Mock provider, int expectedCallCount) { if (provider == null) { throw new ArgumentNullException(nameof(provider)); } - Expression> filterExpression = (string s) => s.Contains("Microsoft\\PowerToys\\settings.json", StringComparison.Ordinal); - IIOProviderMocks.VerifyIOReadWithStubFile(provider, filterExpression, expectedCallCount); + IIOProviderMocks.VerifyIOReadWithStubFile(provider, SettingsFilterExpression, expectedCallCount); } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/CorruptJson/Microsoft/PowerToys/settings.json b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/CorruptJson/Microsoft/PowerToys/settings.json new file mode 100644 index 0000000000..7e9dae7531 Binary files /dev/null and b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/CorruptJson/Microsoft/PowerToys/settings.json differ 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 dfd3da6439..501187832b 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 @@ -29,6 +29,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/IIOProviderMocks.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/IIOProviderMocks.cs index 9a0b7433ec..226e243b08 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/IIOProviderMocks.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/Mocks/IIOProviderMocks.cs @@ -1,10 +1,9 @@ using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Moq; using System; -using System.Collections.Generic; -using System.IO; +using System.IO.Abstractions; +using System.IO.Abstractions.TestingHelpers; using System.Linq.Expressions; -using System.Text; namespace Microsoft.PowerToys.Settings.UI.UnitTests.Mocks { @@ -26,11 +25,13 @@ namespace Microsoft.PowerToys.Settings.UI.UnitTests.Mocks savePath = path; saveContent = content; }); + // Using Ordinal since this is used internally for a path mockIOProvider.Setup(x => x.ReadAllText(It.Is(x => x.Equals(savePath, StringComparison.Ordinal)))) .Returns(() => saveContent); - + // Using Ordinal since this is used internally for a path mockIOProvider.Setup(x => x.FileExists(It.Is(x => x.Equals(savePath, StringComparison.Ordinal)))) .Returns(true); + // Using Ordinal since this is used internally for a path mockIOProvider.Setup(x => x.FileExists(It.Is(x => !x.Equals(savePath, StringComparison.Ordinal)))) .Returns(false); @@ -39,6 +40,8 @@ namespace Microsoft.PowerToys.Settings.UI.UnitTests.Mocks + private static readonly IFileSystem FileSystem = new FileSystem(); + private static readonly IFile File = FileSystem.File; /// /// This method mocks an IO provider so that it will always return data at the savePath location. /// This mock is specific to a given module, and is verifiable that the stub file was read. @@ -46,25 +49,25 @@ namespace Microsoft.PowerToys.Settings.UI.UnitTests.Mocks /// The path to the stub settings file /// The substring in the path that identifies the module eg. Microsoft\\PowerToys\\ColorPicker /// - internal static Mock GetMockIOReadWithStubFile(string savePath, Expression> filterExpression) + internal static Mock GetMockIOReadWithStubFile(string savePath, Expression> filterExpression) { string saveContent = File.ReadAllText(savePath); - var mockIOProvider = new Mock(); + var fileMock = new Mock(); - mockIOProvider.Setup(x => x.ReadAllText(It.Is(filterExpression))) + fileMock.Setup(x => x.ReadAllText(It.Is(filterExpression))) .Returns(() => saveContent).Verifiable(); - mockIOProvider.Setup(x => x.FileExists(It.Is(filterExpression))) + fileMock.Setup(x => x.Exists(It.Is(filterExpression))) .Returns(true); - return mockIOProvider; + return fileMock; } - internal static void VerifyIOReadWithStubFile(Mock mockIOProvider, Expression> filterExpression, int expectedCallCount) + internal static void VerifyIOReadWithStubFile(Mock fileMock, Expression> filterExpression, int expectedCallCount) { - mockIOProvider.Verify(x => x.ReadAllText(It.Is(filterExpression)), Times.Exactly(expectedCallCount)); + fileMock.Verify(x => x.ReadAllText(It.Is(filterExpression)), Times.Exactly(expectedCallCount)); } } } 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 99555c4649..67daf9a363 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/BasePTModuleSettingsTest.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/BasePTModuleSettingsTest.cs @@ -6,9 +6,9 @@ using System; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; +using System.IO.Abstractions.TestingHelpers; using Microsoft.PowerToys.Settings.UnitTest; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Schema; @@ -25,11 +25,8 @@ namespace CommonLibTest public void ToJsonStringShouldReturnValidJSONOfModelWhenSuccessful() { //Mock Disk access - string saveContent = string.Empty; - string savePath = string.Empty; - var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); - - var settingsUtils = new SettingsUtils(mockIOProvider.Object); + var mockFileSystem = new MockFileSystem(); + var settingsUtils = new SettingsUtils(mockFileSystem); // Arrange string file_name = "test\\BasePTModuleSettingsTest"; diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/SettingsRepositoryTest.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/SettingsRepositoryTest.cs index 99501ff2e6..ced10464d9 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/SettingsRepositoryTest.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/SettingsRepositoryTest.cs @@ -2,9 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; -using System.IO; using System.Threading.Tasks; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.VisualStudio.TestTools.UnitTesting; 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 1289f7015d..9c60f9b8b3 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs @@ -3,11 +3,14 @@ // See the LICENSE file in the project root for more information. using System; -using System.IO; +using System.IO.Abstractions; +using System.IO.Abstractions.TestingHelpers; using System.Linq; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.Library.Interfaces; using Microsoft.PowerToys.Settings.UI.Library.Utilities; +using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.PowerToys.Settings.UnitTest; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -19,14 +22,13 @@ namespace CommonLibTest public class SettingsUtilsTests { - [TestMethod] public void SaveSettingsSaveSettingsToFileWhenFilePathExists() { // Arrange - var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); - var settingsUtils = new SettingsUtils(mockIOProvider.Object); - + var mockFileSystem = new MockFileSystem(); + var settingsUtils = new SettingsUtils(mockFileSystem); + string file_name = "\\test"; string file_contents_correct_json_content = "{\"name\":\"powertoy module name\",\"version\":\"powertoy version\"}"; @@ -44,8 +46,8 @@ namespace CommonLibTest public void SaveSettingsShouldCreateFileWhenFilePathIsNotFound() { // Arrange - var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); - var settingsUtils = new SettingsUtils(mockIOProvider.Object); + var mockFileSystem = new MockFileSystem(); + var settingsUtils = new SettingsUtils(mockFileSystem); string file_name = "test\\Test Folder"; string file_contents_correct_json_content = "{\"name\":\"powertoy module name\",\"version\":\"powertoy version\"}"; @@ -62,8 +64,8 @@ namespace CommonLibTest public void SettingsFolderExistsShouldReturnFalseWhenFilePathIsNotFound() { // Arrange - var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); - var settingsUtils = new SettingsUtils(mockIOProvider.Object); + var mockFileSystem = new MockFileSystem(); + var settingsUtils = new SettingsUtils(mockFileSystem); 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\"}"; @@ -79,6 +81,21 @@ namespace CommonLibTest Assert.IsTrue(pathFound); } + [TestMethod] + public void SettingsUtilsMustReturnDefaultItemWhenFileIsCorrupt() + { + // Arrange + var mockFileSystem = new MockFileSystem(); + var mockSettingsUtils = new SettingsUtils(mockFileSystem); + + // Act + TestClass settings = mockSettingsUtils.GetSettings(string.Empty); + + // Assert + Assert.AreEqual(settings.TestInt, 100); + Assert.AreEqual(settings.TestString, "test"); + } + public static string RandomString() { Random random = new Random(); @@ -88,5 +105,26 @@ namespace CommonLibTest return new string(Enumerable.Repeat(chars, length) .Select(s => s[random.Next(s.Length)]).ToArray()); } + + partial class TestClass : ISettingsConfig + { + public int TestInt { get; set; } = 100; + public string TestString { get; set; } = "test"; + + public string GetModuleName() + { + throw new NotImplementedException(); + } + + public string ToJsonString() + { + return JsonSerializer.Serialize(this); + } + + public bool UpgradeSettingsConfiguration() + { + throw new NotImplementedException(); + } + } } } 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 cc649c441e..86f6db4255 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ColorPicker.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.Globalization; -using System.IO; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.ViewModels; using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; namespace ViewModelTests { @@ -27,11 +26,14 @@ namespace ViewModelTests { //Arrange var mockIOProvider = BackCompatTestProperties.GetModuleIOProvider(version, ColorPickerSettings.ModuleName, fileName); - var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); + var settingPathMock = new Mock(); + + var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object, settingPathMock.Object); ColorPickerSettings originalSettings = mockSettingsUtils.GetSettings(ColorPickerSettings.ModuleName); var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); - var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object); + + var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object, settingPathMock.Object); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings(); var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository(mockGeneralSettingsUtils); 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 8f87d1fe0b..8e3a3262df 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/FancyZones.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/FancyZones.cs @@ -3,9 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Drawing; -using System.Globalization; -using System.IO; using System.Text.Json; using CommonLibTest; using Microsoft.PowerToys.Settings.UI.Library; @@ -33,13 +30,16 @@ namespace ViewModelTests [DataRow("v0.22.0", "settings.json")] public void OriginalFilesModificationTest(string version, string fileName) { - var mockIOProvider = BackCompatTestProperties.GetModuleIOProvider(version, FancyZonesSettings.ModuleName, fileName); - var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); + var settingPathMock = new Mock(); + + var fileMock = BackCompatTestProperties.GetModuleIOProvider(version, FancyZonesSettings.ModuleName, fileName); + var mockSettingsUtils = new SettingsUtils(fileMock.Object, settingPathMock.Object); FancyZonesSettings originalSettings = mockSettingsUtils.GetSettings(FancyZonesSettings.ModuleName); var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); - var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object); + var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object, settingPathMock.Object); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings(); + var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository(mockGeneralSettingsUtils); var fancyZonesRepository = new BackCompatTestProperties.MockSettingsRepository(mockSettingsUtils); @@ -71,7 +71,7 @@ namespace ViewModelTests //Verify that the stub file was used var expectedCallCount = 2; //once via the view model, and once by the test (GetSettings) - BackCompatTestProperties.VerifyModuleIOProviderWasRead(mockIOProvider, FancyZonesSettings.ModuleName, expectedCallCount); + BackCompatTestProperties.VerifyModuleIOProviderWasRead(fileMock, FancyZonesSettings.ModuleName, expectedCallCount); BackCompatTestProperties.VerifyGeneralSettingsIOProviderWasRead(mockGeneralIOProvider, expectedCallCount); } 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 bf543c8fcd..425f75f7cf 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/General.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/General.cs @@ -4,7 +4,6 @@ using System; using System.Globalization; -using System.IO; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.ViewModels; @@ -12,7 +11,7 @@ using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; -using NuGet.Frameworks; +using JsonSerializer = System.Text.Json.JsonSerializer; namespace ViewModelTests { @@ -40,16 +39,20 @@ namespace ViewModelTests [DataRow("v0.22.0")] public void OriginalFilesModificationTest(string version) { - var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); - var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object); + var settingPathMock = new Mock(); + var fileMock = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); + + var mockGeneralSettingsUtils = new SettingsUtils(fileMock.Object, settingPathMock.Object); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings(); + var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository(mockGeneralSettingsUtils); + // Initialise View Model with test Config files // Arrange - Func SendMockIPCConfigMSG = msg => { return 0; }; - Func SendRestartAdminIPCMessage = msg => { return 0; }; - Func SendCheckForUpdatesIPCMessage = msg => { return 0; }; + Func SendMockIPCConfigMSG = msg => 0; + Func SendRestartAdminIPCMessage = msg => 0; + Func SendCheckForUpdatesIPCMessage = msg => 0; var viewModel = new GeneralViewModel( settingsRepository: generalSettingsRepository, runAsAdminText: "GeneralSettings_RunningAsAdminText", @@ -71,7 +74,7 @@ namespace ViewModelTests //Verify that the stub file was used var expectedCallCount = 2; //once via the view model, and once by the test (GetSettings) - BackCompatTestProperties.VerifyGeneralSettingsIOProviderWasRead(mockGeneralIOProvider, expectedCallCount); + BackCompatTestProperties.VerifyGeneralSettingsIOProviderWasRead(fileMock, expectedCallCount); } [TestMethod] @@ -82,7 +85,7 @@ namespace ViewModelTests Func SendRestartAdminIPCMessage = msg => { return 0; }; Func SendCheckForUpdatesIPCMessage = msg => { return 0; }; GeneralViewModel viewModel = new GeneralViewModel( - SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), + settingsRepository: SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), "GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsUserText", false, @@ -119,7 +122,7 @@ namespace ViewModelTests Func SendRestartAdminIPCMessage = msg => { return 0; }; Func SendCheckForUpdatesIPCMessage = msg => { return 0; }; GeneralViewModel viewModel = new GeneralViewModel( - SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), + settingsRepository: SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), "GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsUserText", false, @@ -151,7 +154,7 @@ namespace ViewModelTests // Arrange GeneralViewModel viewModel = new GeneralViewModel( - SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), + settingsRepository: SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), "GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsUserText", false, @@ -184,7 +187,7 @@ namespace ViewModelTests Func SendRestartAdminIPCMessage = msg => { return 0; }; Func SendCheckForUpdatesIPCMessage = msg => { return 0; }; viewModel = new GeneralViewModel( - SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), + settingsRepository: SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), "GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsUserText", false, @@ -215,7 +218,7 @@ namespace ViewModelTests Func SendRestartAdminIPCMessage = msg => { return 0; }; Func SendCheckForUpdatesIPCMessage = msg => { return 0; }; GeneralViewModel viewModel = new GeneralViewModel( - SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), + settingsRepository: 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 5ebf32f6e0..62eed7965a 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ImageResizer.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ImageResizer.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System; -using System.Globalization; -using System.IO; +using System.IO.Abstractions; +using System.IO.Abstractions.TestingHelpers; using System.Linq; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Library; @@ -20,16 +20,15 @@ namespace ViewModelTests [TestClass] public class ImageResizer { + private Mock _mockGeneralSettingsUtils; - private Mock mockGeneralSettingsUtils; - - private Mock mockImgResizerSettingsUtils; + private Mock _mockImgResizerSettingsUtils; [TestInitialize] public void SetUpStubSettingUtils() { - mockGeneralSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); - mockImgResizerSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + _mockGeneralSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + _mockImgResizerSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); } @@ -44,13 +43,17 @@ namespace ViewModelTests [DataRow("v0.22.0", "settings.json")] public void OriginalFilesModificationTest(string version, string fileName) { - var mockIOProvider = BackCompatTestProperties.GetModuleIOProvider(version, ImageResizerSettings.ModuleName, fileName); - var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); + var settingPathMock = new Mock(); + + var fileMock = BackCompatTestProperties.GetModuleIOProvider(version, ImageResizerSettings.ModuleName, fileName); + var mockSettingsUtils = new SettingsUtils(fileMock.Object, settingPathMock.Object); + ImageResizerSettings originalSettings = mockSettingsUtils.GetSettings(ImageResizerSettings.ModuleName); - var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); - var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object); + var mockGeneralFileMock = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); + var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralFileMock.Object, settingPathMock.Object); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings(); + var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository(mockGeneralSettingsUtils); // Initialise View Model with test Config files @@ -69,8 +72,8 @@ namespace ViewModelTests //Verify that the stub file was used var expectedCallCount = 2; //once via the view model, and once by the test (GetSettings) - BackCompatTestProperties.VerifyModuleIOProviderWasRead(mockIOProvider, ImageResizerSettings.ModuleName, expectedCallCount); - BackCompatTestProperties.VerifyGeneralSettingsIOProviderWasRead(mockGeneralIOProvider, expectedCallCount); + BackCompatTestProperties.VerifyModuleIOProviderWasRead(fileMock, ImageResizerSettings.ModuleName, expectedCallCount); + BackCompatTestProperties.VerifyGeneralSettingsIOProviderWasRead(mockGeneralFileMock, expectedCallCount); } [TestMethod] @@ -85,7 +88,7 @@ namespace ViewModelTests }; // arrange - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockImgResizerSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(_mockImgResizerSettingsUtils.Object, SettingsRepository.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); // act viewModel.IsEnabled = true; @@ -95,16 +98,16 @@ namespace ViewModelTests public void JPEGQualityLevelShouldSetValueToTenWhenSuccessful() { // arrange - var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); - var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); + var fileSystemMock = new MockFileSystem(); + var mockSettingsUtils = new SettingsUtils(fileSystemMock); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); // act viewModel.JPEGQualityLevel = 10; // Assert - viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); + viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); Assert.AreEqual(10, viewModel.JPEGQualityLevel); } @@ -112,16 +115,16 @@ namespace ViewModelTests public void PngInterlaceOptionShouldSetValueToTenWhenSuccessful() { // arrange - var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); - var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); + var fileSystemMock = new MockFileSystem(); + var mockSettingsUtils = new SettingsUtils(fileSystemMock); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); // act viewModel.PngInterlaceOption = 10; // Assert - viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); + viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); Assert.AreEqual(10, viewModel.PngInterlaceOption); } @@ -129,16 +132,16 @@ namespace ViewModelTests public void TiffCompressOptionShouldSetValueToTenWhenSuccessful() { // arrange - var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); - var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); + var fileSystemMock = new MockFileSystem(); + var mockSettingsUtils = new SettingsUtils(fileSystemMock); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); // act viewModel.TiffCompressOption = 10; // Assert - viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); + viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); Assert.AreEqual(10, viewModel.TiffCompressOption); } @@ -146,17 +149,17 @@ namespace ViewModelTests public void FileNameShouldUpdateValueWhenSuccessful() { // arrange - var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); - var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); + var fileSystemMock = new MockFileSystem(); + var mockSettingsUtils = new SettingsUtils(fileSystemMock); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); string expectedValue = "%1 (%3)"; // act viewModel.FileName = expectedValue; // Assert - viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); + viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); Assert.AreEqual(expectedValue, viewModel.FileName); } @@ -167,6 +170,7 @@ namespace ViewModelTests var settingUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); var expectedSettingsString = new ImageResizerSettings() { Properties = new ImageResizerProperties() { ImageresizerKeepDateModified = new BoolProperty() { Value = true } } }.ToJsonString(); + // Using Ordinal since this is used internally settingUtils.Setup(x => x.SaveSettings( It.Is(content => content.Equals(expectedSettingsString, StringComparison.Ordinal)), It.Is(module => module.Equals(ImageResizerSettings.ModuleName, StringComparison.Ordinal)), @@ -174,7 +178,7 @@ namespace ViewModelTests .Verifiable(); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(settingUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(settingUtils.Object, SettingsRepository.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); // act viewModel.KeepDateModified = true; @@ -187,16 +191,16 @@ namespace ViewModelTests public void EncoderShouldUpdateValueWhenSuccessful() { // arrange - var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); - var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); + var fileSystemMock = new MockFileSystem(); + var mockSettingsUtils = new SettingsUtils(fileSystemMock); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); // act viewModel.Encoder = 3; // Assert - viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); + viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); Assert.AreEqual("163bcc30-e2e9-4f0b-961d-a3e9fdb788a3", viewModel.EncoderGuid); Assert.AreEqual(3, viewModel.Encoder); } @@ -207,7 +211,7 @@ namespace ViewModelTests // arrange var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); + ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); int sizeOfOriginalArray = viewModel.Sizes.Count; // act @@ -223,7 +227,7 @@ namespace ViewModelTests // arrange var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); Func SendMockIPCConfigMSG = msg => { return 0; }; - ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils.Object, SettingsRepository.GetInstance(mockGeneralSettingsUtils.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/PowerLauncherViewModelTest.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs index bd180e830c..f6a405f19a 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs @@ -10,6 +10,7 @@ using Moq; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility; using System.Globalization; +using System.IO.Abstractions; namespace ViewModelTests { @@ -52,13 +53,16 @@ namespace ViewModelTests [DataRow("v0.22.0", "settings.json")] public void OriginalFilesModificationTest(string version, string fileName) { + var settingPathMock = new Mock(); + var mockIOProvider = BackCompatTestProperties.GetModuleIOProvider(version, PowerLauncherSettings.ModuleName, fileName); - var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); + var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object, settingPathMock.Object); PowerLauncherSettings originalSettings = mockSettingsUtils.GetSettings(PowerLauncherSettings.ModuleName); var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); - var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object); + var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object, settingPathMock.Object); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings(); + var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository(mockGeneralSettingsUtils); // Initialise View Model with test Config files 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 d66a6839e0..4564578c89 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.IO; +using System.IO.Abstractions; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.ViewModels; @@ -40,13 +40,15 @@ namespace ViewModelTests [DataRow("v0.22.0", "settings.json")] public void OriginalFilesModificationTest(string version, string fileName) { - var mockIOProvider = BackCompatTestProperties.GetModuleIOProvider(version, PowerPreviewSettings.ModuleName, fileName); - var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); + var settingPathMock = new Mock(); + var fileMock = BackCompatTestProperties.GetModuleIOProvider(version, PowerPreviewSettings.ModuleName, fileName); + + var mockSettingsUtils = new SettingsUtils(fileMock.Object, settingPathMock.Object); PowerPreviewSettings originalSettings = mockSettingsUtils.GetSettings(PowerPreviewSettings.ModuleName); var repository = new BackCompatTestProperties.MockSettingsRepository(mockSettingsUtils); var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); - var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object); + var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object, settingPathMock.Object); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings(); var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository(mockGeneralSettingsUtils); @@ -62,7 +64,7 @@ namespace ViewModelTests //Verify that the stub file was used var expectedCallCount = 2; //once via the view model, and once by the test (GetSettings) - BackCompatTestProperties.VerifyModuleIOProviderWasRead(mockIOProvider, PowerPreviewSettings.ModuleName, expectedCallCount); + BackCompatTestProperties.VerifyModuleIOProviderWasRead(fileMock, PowerPreviewSettings.ModuleName, expectedCallCount); } 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 794dd1b972..efaa2710c6 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerRename.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerRename.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.IO; +using System.IO.Abstractions; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.ViewModels; @@ -40,12 +40,16 @@ namespace ViewModelTests [DataRow("v0.22.0", "power-rename-settings.json")] public void OriginalFilesModificationTest(string version, string fileName) { + var settingPathMock = new Mock(); var mockIOProvider = BackCompatTestProperties.GetModuleIOProvider(version, PowerRenameSettings.ModuleName, fileName); - var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); + + var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object, settingPathMock.Object); PowerRenameLocalProperties originalSettings = mockSettingsUtils.GetSettings(PowerRenameSettings.ModuleName); + var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); - var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object); + + var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object, settingPathMock.Object); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings(); var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository(mockGeneralSettingsUtils); 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 0e4fb665dd..fbdd209adc 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.IO; using System.Text.Json; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.ViewModels; @@ -30,12 +29,13 @@ namespace ViewModelTests [DataRow("v0.22.0", "settings.json")] public void OriginalFilesModificationTest(string version, string fileName) { + var settingPathMock = new Mock(); var mockIOProvider = BackCompatTestProperties.GetModuleIOProvider(version, ShortcutGuideSettings.ModuleName, fileName); - var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); + var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object, settingPathMock.Object); ShortcutGuideSettings originalSettings = mockSettingsUtils.GetSettings(ShortcutGuideSettings.ModuleName); var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); - var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object); + var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object, settingPathMock.Object); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings(); var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository(mockGeneralSettingsUtils); var shortcutSettingsRepository = new BackCompatTestProperties.MockSettingsRepository(mockSettingsUtils); diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Activation/ActivationHandler.cs b/src/core/Microsoft.PowerToys.Settings.UI/Activation/ActivationHandler.cs index fe71a7ca37..aabe2aff53 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Activation/ActivationHandler.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Activation/ActivationHandler.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.Diagnostics.CodeAnalysis; using System.Threading.Tasks; namespace Microsoft.PowerToys.Settings.UI.Activation @@ -15,13 +16,13 @@ namespace Microsoft.PowerToys.Settings.UI.Activation public abstract Task HandleAsync(object args); } - [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "abstract T and abstract")] + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "abstract T and abstract")] internal abstract class ActivationHandler : ActivationHandler where T : class { public override async Task HandleAsync(object args) { - await HandleInternalAsync(args as T); + await HandleInternalAsync(args as T).ConfigureAwait(false); } public override bool CanHandle(object args) diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Activation/DefaultActivationHandler.cs b/src/core/Microsoft.PowerToys.Settings.UI/Activation/DefaultActivationHandler.cs index 5be096f9fc..2c401c4a39 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Activation/DefaultActivationHandler.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Activation/DefaultActivationHandler.cs @@ -29,7 +29,7 @@ namespace Microsoft.PowerToys.Settings.UI.Activation } NavigationService.Navigate(navElement, arguments); - await Task.CompletedTask; + await Task.CompletedTask.ConfigureAwait(false); } protected override bool CanHandleInternal(IActivatedEventArgs args) diff --git a/src/core/Microsoft.PowerToys.Settings.UI/App.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/App.xaml.cs index a90278c897..e54e7f6a48 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/App.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/App.xaml.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 Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.Toolkit.Win32.UI.XamlHost; namespace Microsoft.PowerToys.Settings.UI @@ -15,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI // Hide the Xaml Island window var coreWindow = Windows.UI.Core.CoreWindow.GetForCurrentThread(); var coreWindowInterop = Interop.GetInterop(coreWindow); - Interop.ShowWindow(coreWindowInterop.WindowHandle, Interop.SW_HIDE); + NativeMethods.ShowWindow(coreWindowInterop.WindowHandle, Interop.SW_HIDE); } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Behaviors/NavigationViewHeaderBehavior.cs b/src/core/Microsoft.PowerToys.Settings.UI/Behaviors/NavigationViewHeaderBehavior.cs index 314c238e74..41eaea560f 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Behaviors/NavigationViewHeaderBehavior.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Behaviors/NavigationViewHeaderBehavior.cs @@ -30,12 +30,12 @@ namespace Microsoft.PowerToys.Settings.UI.Behaviors public static NavigationViewHeaderMode GetHeaderMode(Page item) { - return (NavigationViewHeaderMode)item.GetValue(HeaderModeProperty); + return (NavigationViewHeaderMode)item?.GetValue(HeaderModeProperty); } public static void SetHeaderMode(Page item, NavigationViewHeaderMode value) { - item.SetValue(HeaderModeProperty, value); + item?.SetValue(HeaderModeProperty, value); } public static readonly DependencyProperty HeaderModeProperty = @@ -43,12 +43,12 @@ namespace Microsoft.PowerToys.Settings.UI.Behaviors public static object GetHeaderContext(Page item) { - return item.GetValue(HeaderContextProperty); + return item?.GetValue(HeaderContextProperty); } public static void SetHeaderContext(Page item, object value) { - item.SetValue(HeaderContextProperty, value); + item?.SetValue(HeaderContextProperty, value); } public static readonly DependencyProperty HeaderContextProperty = @@ -56,12 +56,12 @@ namespace Microsoft.PowerToys.Settings.UI.Behaviors public static DataTemplate GetHeaderTemplate(Page item) { - return (DataTemplate)item.GetValue(HeaderTemplateProperty); + return (DataTemplate)item?.GetValue(HeaderTemplateProperty); } public static void SetHeaderTemplate(Page item, DataTemplate value) { - item.SetValue(HeaderTemplateProperty, value); + item?.SetValue(HeaderTemplateProperty, value); } public static readonly DependencyProperty HeaderTemplateProperty = diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Controls/HotkeySettingsControl.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Controls/HotkeySettingsControl.xaml.cs index b7540b907e..3711bf03ca 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Controls/HotkeySettingsControl.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Controls/HotkeySettingsControl.xaml.cs @@ -11,13 +11,13 @@ using Windows.UI.Xaml.Controls; namespace Microsoft.PowerToys.Settings.UI.Controls { - public sealed partial class HotkeySettingsControl : UserControl + public sealed partial class HotkeySettingsControl : UserControl, IDisposable { private readonly UIntPtr ignoreKeyEventFlag = (UIntPtr)0x5555; - private bool _shiftKeyDownOnEntering = false; + private bool _shiftKeyDownOnEntering; - private bool _shiftToggled = false; + private bool _shiftToggled; public string Header { get; set; } @@ -30,7 +30,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls typeof(HotkeySettingsControl), null); - private bool _enabled = false; + private bool _enabled; public bool Enabled { @@ -73,6 +73,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls private HotkeySettings lastValidSettings; private HotkeySettingsControlHook hook; private bool _isActive; + private bool disposedValue; public HotkeySettings HotkeySettings { @@ -109,7 +110,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls hook.Dispose(); } - private void KeyEventHandler(int key, bool matchValue, int matchValueCode, string matchValueText) + private void KeyEventHandler(int key, bool matchValue, int matchValueCode) { switch ((Windows.System.VirtualKey)key) { @@ -229,7 +230,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls { await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { - KeyEventHandler(key, true, key, Library.Utilities.Helper.GetKeyName((uint)key)); + KeyEventHandler(key, true, key); // Tab and Shift+Tab are accessible keys and should not be displayed in the hotkey control. if (internalSettings.Code > 0 && !internalSettings.IsAccessibleShortcut()) @@ -244,7 +245,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls { await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { - KeyEventHandler(key, false, 0, string.Empty); + KeyEventHandler(key, false, 0); }); } @@ -278,5 +279,28 @@ namespace Microsoft.PowerToys.Settings.UI.Controls HotkeyTextBox.Text = hotkeySettings.ToString(); _isActive = false; } + + private void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + hook.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/core/Microsoft.PowerToys.Settings.UI/Converters/ModuleEnabledToForegroundConverter.cs b/src/core/Microsoft.PowerToys.Settings.UI/Converters/ModuleEnabledToForegroundConverter.cs index 7b1feee775..e35908e095 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Converters/ModuleEnabledToForegroundConverter.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Converters/ModuleEnabledToForegroundConverter.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Globalization; using Microsoft.PowerToys.Settings.UI.Library; -using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Windows.UI.Xaml; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Media; @@ -13,7 +13,7 @@ namespace Microsoft.PowerToys.Settings.UI.Converters { public sealed class ModuleEnabledToForegroundConverter : IValueConverter { - private readonly ISettingsUtils settingsUtils = new SettingsUtils(new SystemIOProvider()); + private readonly ISettingsUtils settingsUtils = new SettingsUtils(); private string selectedTheme = string.Empty; @@ -22,10 +22,14 @@ namespace Microsoft.PowerToys.Settings.UI.Converters bool isEnabled = (bool)value; var defaultTheme = new Windows.UI.ViewManagement.UISettings(); - var uiTheme = defaultTheme.GetColorValue(Windows.UI.ViewManagement.UIColorType.Background).ToString(); - selectedTheme = SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Theme.ToLower(); - if (selectedTheme == "dark" || (selectedTheme == "system" && uiTheme == "#FF000000")) + // Using InvariantCulture as this is an internal string and expected to be in hexadecimal + var uiTheme = defaultTheme.GetColorValue(Windows.UI.ViewManagement.UIColorType.Background).ToString(CultureInfo.InvariantCulture); + + // Normalize strings to uppercase according to Fxcop + selectedTheme = SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Theme.ToUpperInvariant(); + + if (selectedTheme == "DARK" || (selectedTheme == "SYSTEM" && uiTheme == "#FF000000")) { // DARK if (isEnabled) diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Helpers/NativeMethods.cs b/src/core/Microsoft.PowerToys.Settings.UI/Helpers/NativeMethods.cs index 9ed4efe3b2..4f91dd4b4b 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Helpers/NativeMethods.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Helpers/NativeMethods.cs @@ -13,5 +13,8 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern short GetAsyncKeyState(int vKey); + + [DllImport("user32.dll")] + public static extern bool ShowWindow(System.IntPtr hWnd, int nCmdShow); } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Helpers/NavHelper.cs b/src/core/Microsoft.PowerToys.Settings.UI/Helpers/NavHelper.cs index 8759d4a7a6..54d21c66e1 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Helpers/NavHelper.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Helpers/NavHelper.cs @@ -8,7 +8,7 @@ using Windows.UI.Xaml; namespace Microsoft.PowerToys.Settings.UI.Helpers { - public class NavHelper + public static class NavHelper { // This helper class allows to specify the page that will be shown when you click on a NavigationViewItem // @@ -19,12 +19,12 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers // NavHelper.SetNavigateTo(navigationViewItem, typeof(MainPage)); public static Type GetNavigateTo(NavigationViewItem item) { - return (Type)item.GetValue(NavigateToProperty); + return (Type)item?.GetValue(NavigateToProperty); } public static void SetNavigateTo(NavigationViewItem item, Type value) { - item.SetValue(NavigateToProperty, value); + item?.SetValue(NavigateToProperty, value); } public static readonly DependencyProperty NavigateToProperty = diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Interop.cs b/src/core/Microsoft.PowerToys.Settings.UI/Interop.cs index f56e74f457..2ad765b37f 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Interop.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Interop.cs @@ -23,9 +23,6 @@ namespace Microsoft.PowerToys.Settings.UI } } - [DllImport("user32.dll")] - public static extern bool ShowWindow(System.IntPtr hWnd, int nCmdShow); - [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Interop naming consistancy")] public const int SW_HIDE = 0; } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Microsoft.PowerToys.Settings.UI.csproj b/src/core/Microsoft.PowerToys.Settings.UI/Microsoft.PowerToys.Settings.UI.csproj index 12e2281d14..e089cc93f7 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Microsoft.PowerToys.Settings.UI.csproj +++ b/src/core/Microsoft.PowerToys.Settings.UI/Microsoft.PowerToys.Settings.UI.csproj @@ -201,6 +201,11 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + + 3.3.0 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Services/ActivationService.cs b/src/core/Microsoft.PowerToys.Settings.UI/Services/ActivationService.cs index 55d7af020c..e94c3596fb 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Services/ActivationService.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Services/ActivationService.cs @@ -38,7 +38,7 @@ namespace Microsoft.PowerToys.Settings.UI.Services { // Initialize services that you need before app activation // take into account that the splash screen is shown while this code runs. - await InitializeAsync(); + await InitializeAsync().ConfigureAwait(false); // Do not repeat app initialization when the Window already has content, // just ensure that the window is active @@ -51,7 +51,7 @@ namespace Microsoft.PowerToys.Settings.UI.Services // Depending on activationArgs one of ActivationHandlers or DefaultActivationHandler // will navigate to the first page - await HandleActivationAsync(activationArgs); + await HandleActivationAsync(activationArgs).ConfigureAwait(false); lastActivationArgs = activationArgs; if (IsInteractive(activationArgs)) @@ -60,13 +60,13 @@ namespace Microsoft.PowerToys.Settings.UI.Services Window.Current.Activate(); // Tasks after activation - await StartupAsync(); + await StartupAsync().ConfigureAwait(false); } } - private async Task InitializeAsync() + private static async Task InitializeAsync() { - await Task.CompletedTask; + await Task.CompletedTask.ConfigureAwait(false); } private async Task HandleActivationAsync(object activationArgs) @@ -76,7 +76,7 @@ namespace Microsoft.PowerToys.Settings.UI.Services if (activationHandler != null) { - await activationHandler.HandleAsync(activationArgs); + await activationHandler.HandleAsync(activationArgs).ConfigureAwait(false); } if (IsInteractive(activationArgs)) @@ -84,22 +84,22 @@ namespace Microsoft.PowerToys.Settings.UI.Services var defaultHandler = new DefaultActivationHandler(defaultNavItem); if (defaultHandler.CanHandle(activationArgs)) { - await defaultHandler.HandleAsync(activationArgs); + await defaultHandler.HandleAsync(activationArgs).ConfigureAwait(false); } } } - private async Task StartupAsync() + private static async Task StartupAsync() { - await Task.CompletedTask; + await Task.CompletedTask.ConfigureAwait(false); } - private IEnumerable GetActivationHandlers() + private static IEnumerable GetActivationHandlers() { yield break; } - private bool IsInteractive(object args) + private static bool IsInteractive(object args) { return args is IActivatedEventArgs; } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw b/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw index 503fcc696f..83c908a3ee 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw +++ b/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw @@ -117,53 +117,41 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Microsoft.PowerToys.Settings.UI - Application display name - - - Microsoft.PowerToys.Settings.UI - Application description - - - Main - Navigation view item name for Main - General Navigation view item name for General PowerToys Run - Navigation view item name for PowerToys Run + Product name: Navigation view item name for PowerToys Run PowerRename - Navigation view item name for PowerRename + Product name: Navigation view item name for PowerRename Shortcut Guide - Navigation view item name for Shortcut Guide + Product name: Navigation view item name for Shortcut Guide File Explorer - Navigation view item name for File Explorer Preview + Product name: Navigation view item name for File Explorer Preview FancyZones - Navigation view item name for FancyZones + Product name: Navigation view item name for FancyZones Image Resizer - Navigation view item name for Image Resizer + Product name: Navigation view item name for Image Resizer Color Picker - Navigation view item name for Color Picker + Product name: Navigation view item name for Color Picker Keyboard Manager - Navigation view item name for Keyboard Manager + Product name: Navigation view item name for Keyboard Manager Current configuration @@ -175,7 +163,10 @@ Enable Keyboard Manager - Keyboard Manager enable toggle header + + Keyboard Manager enable toggle header + do not loc the Product name. Do you want this feature on / off + Select the profile to display the active key remap and shortcuts @@ -226,6 +217,7 @@ Enable Color Picker + do not loc the Product name. Do you want this feature on / off Change cursor when picking a color @@ -241,6 +233,7 @@ Enable PowerToys Run + do not loc the Product name. Do you want this feature on / off Search & results @@ -323,6 +316,7 @@ Enable FancyZones + do not loc the Product name. Do you want this feature on / off Excluded apps @@ -385,10 +379,12 @@ Give feedback - Module overview + Learn more + This label is there to point people to additional overview for how to use the product Attribution + giving credit to the projects this utility was based on About PowerToys @@ -404,12 +400,15 @@ Report a bug + Report an issue inside powertoys Request a feature + Tell our team what we should build Restart as administrator + running PowerToys as a higher level user, account is typically referred to as an admin / administrator Run at startup @@ -419,9 +418,11 @@ Shell integration + This refers to directly integrating in with Windows Enable PowerRename + do not loc the Product name. Do you want this feature on / off Settings theme @@ -440,12 +441,15 @@ Enable Markdown (.md) preview + Do not loc "Markdown". Do you want this feature on / off Enable SVG (.svg) preview + Do you want this feature on / off Enable SVG (.svg) thumbnails + Do you want this feature on / off These settings allow you to manage your Windows File Explorer custom preview handlers. @@ -476,6 +480,7 @@ Enable Shortcut Guide + do not loc the Product name. Do you want this feature on / off Opacity of background @@ -488,6 +493,7 @@ Enable Image Resizer + do not loc the Product name. Do you want this feature on / off Image Size @@ -770,11 +776,14 @@ Light + Light refers to color, not weight Windows default + Windows refers to the Operating system Windows color settings + Windows refers to the Operating system - \ No newline at end of file + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/ViewModels/ShellViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI/ViewModels/ShellViewModel.cs index be4efd8576..2750930b7d 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/ViewModels/ShellViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/ViewModels/ShellViewModel.cs @@ -84,7 +84,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels // More info on tracking issue https://github.com/Microsoft/microsoft-ui-xaml/issues/8 keyboardAccelerators.Add(altLeftKeyboardAccelerator); keyboardAccelerators.Add(backKeyboardAccelerator); - await Task.CompletedTask; + await Task.CompletedTask.ConfigureAwait(false); } private void OnItemInvoked(WinUI.NavigationViewItemInvokedEventArgs args) @@ -114,7 +114,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels .FirstOrDefault(menuItem => IsMenuItemForPageType(menuItem, e.SourcePageType)); } - private bool IsMenuItemForPageType(WinUI.NavigationViewItem menuItem, Type sourcePageType) + private static bool IsMenuItemForPageType(WinUI.NavigationViewItem menuItem, Type sourcePageType) { var pageType = menuItem.GetValue(NavHelper.NavigateToProperty) as Type; return pageType == sourcePageType; diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml index 1a64bf15a5..7fb9e40c5d 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml @@ -9,7 +9,8 @@ mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" + AutomationProperties.LandmarkType="Main"> 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 e27aeb3aaa..6d994eedc2 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,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.IO.Abstractions; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.Library.ViewModels; @@ -15,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public ColorPickerPage() { - var settingsUtils = new SettingsUtils(new SystemIOProvider()); + var settingsUtils = new SettingsUtils(); ViewModel = new ColorPickerViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; InitializeComponent(); diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml index ea44240466..91eb9221de 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml @@ -12,7 +12,8 @@ xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core" mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" + AutomationProperties.LandmarkType="Main"> 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 bf96e0880d..f8687cfd1c 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/FancyZonesPage.xaml.cs @@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public FancyZonesPage() { InitializeComponent(); - var settingsUtils = new SettingsUtils(new SystemIOProvider()); + var settingsUtils = new SettingsUtils(); 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 b/src/core/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml index 290e5f442a..f9f392323a 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml @@ -8,7 +8,8 @@ xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters" xmlns:muxc="using:Microsoft.UI.Xaml.Controls" mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" + AutomationProperties.LandmarkType="Main"> 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 f177989ae4..9ee9591d36 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 System.Diagnostics.CodeAnalysis; +using System.Globalization; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.Library.ViewModels; @@ -27,13 +29,14 @@ namespace Microsoft.PowerToys.Settings.UI.Views /// Initializes a new instance of the class. /// General Settings page constructor. /// + [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exceptions from the IPC response handler should be caught and logged.")] public GeneralPage() { InitializeComponent(); // Load string resources ResourceLoader loader = ResourceLoader.GetForViewIndependentUse(); - var settingsUtils = new SettingsUtils(new SystemIOProvider()); + var settingsUtils = new SettingsUtils(); ViewModel = new GeneralViewModel( SettingsRepository.GetInstance(settingsUtils), @@ -58,28 +61,30 @@ namespace Microsoft.PowerToys.Settings.UI.Views { str = ResourceLoader.GetForCurrentView().GetString("GeneralSettings_VersionIsLatest"); } - else if (version != string.Empty) + else if (!string.IsNullOrEmpty(version)) { str = ResourceLoader.GetForCurrentView().GetString("GeneralSettings_NewVersionIsAvailable"); - if (str != string.Empty) + if (!string.IsNullOrEmpty(str)) { str += ": " + version; } } - ViewModel.LatestAvailableVersion = string.Format(str); + // Using CurrentCulture since this is user-facing + ViewModel.LatestAvailableVersion = string.Format(CultureInfo.CurrentCulture, str); } - catch (Exception) + catch (Exception e) { + Logger.LogError("Exception encountered when reading the version.", e); } }); DataContext = ViewModel; } - public int UpdateUIThemeMethod(string themeName) + public static int UpdateUIThemeMethod(string themeName) { - switch (themeName.ToUpperInvariant()) + switch (themeName?.ToUpperInvariant()) { case "LIGHT": ShellPage.ShellHandler.RequestedTheme = ElementTheme.Light; @@ -90,6 +95,9 @@ namespace Microsoft.PowerToys.Settings.UI.Views case "SYSTEM": ShellPage.ShellHandler.RequestedTheme = ElementTheme.Default; break; + default: + Logger.LogError($"Unexpected theme name: {themeName}"); + break; } return 0; diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml index 756db0b648..82f76e6877 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ImageResizerPage.xaml @@ -9,7 +9,8 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:muxc="using:Microsoft.UI.Xaml.Controls" mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" + AutomationProperties.LandmarkType="Main"> 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 ee8fe123d2..c76eeede5c 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,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.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Linq; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; @@ -18,35 +21,41 @@ namespace Microsoft.PowerToys.Settings.UI.Views public ImageResizerPage() { InitializeComponent(); - var settingsUtils = new SettingsUtils(new SystemIOProvider()); + var settingsUtils = new SettingsUtils(); ViewModel = new ImageResizerViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } public void DeleteCustomSize(object sender, RoutedEventArgs e) { - try + Button deleteRowButton = (Button)sender; + + // Using InvariantCulture since this is internal and expected to be numerical + bool success = int.TryParse(deleteRowButton?.CommandParameter?.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out int rowNum); + if (success) { - Button deleteRowButton = (Button)sender; - int rowNum = int.Parse(deleteRowButton.CommandParameter.ToString()); ViewModel.DeleteImageSize(rowNum); } - catch + else { + Logger.LogError("Failed to delete custom image size."); } } + [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "JSON exceptions from saving new settings should be caught and logged.")] private void AddSizeButton_Click(object sender, RoutedEventArgs e) { try { ViewModel.AddRow(); } - catch + catch (Exception ex) { + Logger.LogError("Exception encountered when adding a new image size.", ex); } } + [SuppressMessage("Usage", "CA1801:Review unused parameters", Justification = "Params are required for event handler signature requirements.")] private void ImagesSizesListView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args) { if (ViewModel.IsListViewFocusRequested) diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/KeyboardManagerPage.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Views/KeyboardManagerPage.xaml index 1dfd06a489..f7803d4c8c 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/KeyboardManagerPage.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/KeyboardManagerPage.xaml @@ -10,7 +10,8 @@ xmlns:CustomControls="using:Microsoft.PowerToys.Settings.UI.Controls" xmlns:Lib="using:Microsoft.PowerToys.Settings.UI.Library" mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" + AutomationProperties.LandmarkType="Main"> 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 5fbb40fec3..15a01e9d4f 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/KeyboardManagerPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/KeyboardManagerPage.xaml.cs @@ -4,7 +4,8 @@ using System; using System.Collections.Generic; -using System.IO; +using System.Globalization; +using System.IO.Abstractions; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.Library.ViewModels; @@ -23,7 +24,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views private const string PowerToyName = "Keyboard Manager"; private readonly CoreDispatcher dispatcher; - private readonly FileSystemWatcher watcher; + private readonly IFileSystemWatcher watcher; public KeyboardManagerViewModel ViewModel { get; } @@ -31,7 +32,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views { dispatcher = Window.Current.Dispatcher; - var settingsUtils = new SettingsUtils(new SystemIOProvider()); + var settingsUtils = new SettingsUtils(); ViewModel = new KeyboardManagerViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, FilterRemapKeysList); watcher = Helper.GetFileWatcher( @@ -56,17 +57,18 @@ namespace Microsoft.PowerToys.Settings.UI.Views } } - private void CombineRemappings(List remapKeysList, uint leftKey, uint rightKey, uint combinedKey) + private static void CombineRemappings(List remapKeysList, uint leftKey, uint rightKey, uint combinedKey) { - KeysDataModel firstRemap = remapKeysList.Find(x => uint.Parse(x.OriginalKeys) == leftKey); - KeysDataModel secondRemap = remapKeysList.Find(x => uint.Parse(x.OriginalKeys) == rightKey); + // Using InvariantCulture for keys as they are internally represented as numerical values + KeysDataModel firstRemap = remapKeysList.Find(x => uint.Parse(x.OriginalKeys, CultureInfo.InvariantCulture) == leftKey); + KeysDataModel secondRemap = remapKeysList.Find(x => uint.Parse(x.OriginalKeys, CultureInfo.InvariantCulture) == rightKey); if (firstRemap != null && secondRemap != null) { if (firstRemap.NewRemapKeys == secondRemap.NewRemapKeys) { KeysDataModel combinedRemap = new KeysDataModel { - OriginalKeys = combinedKey.ToString(), + OriginalKeys = combinedKey.ToString(CultureInfo.InvariantCulture), NewRemapKeys = firstRemap.NewRemapKeys, }; remapKeysList.Insert(remapKeysList.IndexOf(firstRemap), combinedRemap); diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml index 5705122f22..4a394e2316 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml @@ -9,7 +9,8 @@ xmlns:viewModel="using:Microsoft.PowerToys.Settings.UI.ViewModels" xmlns:CustomControls="using:Microsoft.PowerToys.Settings.UI.Controls" mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" + AutomationProperties.LandmarkType="Main"> 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 59a61ac699..2efd6c7629 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml.cs @@ -21,7 +21,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public PowerLauncherPage() { InitializeComponent(); - var settingsUtils = new SettingsUtils(new SystemIOProvider()); + var settingsUtils = new SettingsUtils(); ViewModel = new PowerLauncherViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, (int)Windows.System.VirtualKey.Space); DataContext = ViewModel; diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml index 4373d549b4..511292997f 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml @@ -6,7 +6,8 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters" mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" + AutomationProperties.LandmarkType="Main"> 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 0e23644bdb..ee760a37e1 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml.cs @@ -19,7 +19,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public PowerPreviewPage() { InitializeComponent(); - var settingsUtils = new SettingsUtils(new SystemIOProvider()); + var settingsUtils = new SettingsUtils(); ViewModel = new PowerPreviewViewModel(SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerRenamePage.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerRenamePage.xaml index 231a54d622..66bb16619e 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerRenamePage.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerRenamePage.xaml @@ -7,7 +7,8 @@ xmlns:CustomControls="using:Microsoft.PowerToys.Settings.UI.Controls" xmlns:muxc="using:Microsoft.UI.Xaml.Controls" mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" + AutomationProperties.LandmarkType="Main"> 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 dd8bec2564..c0f9a1b514 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerRenamePage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerRenamePage.xaml.cs @@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views public PowerRenamePage() { InitializeComponent(); - var settingsUtils = new SettingsUtils(new SystemIOProvider()); + var settingsUtils = new SettingsUtils(); 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 e7f3d6166c..482e85d7b1 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ShellPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ShellPage.xaml.cs @@ -92,7 +92,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views /// Set Default IPC Message callback function. /// /// delegate function implementation. - public void SetDefaultSndMessageCallback(IPCMessageCallback implementation) + public static void SetDefaultSndMessageCallback(IPCMessageCallback implementation) { DefaultSndMSGCallback = implementation; } @@ -101,7 +101,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views /// Set restart as admin IPC callback function. /// /// delegate function implementation. - public void SetRestartAdminSndMessageCallback(IPCMessageCallback implementation) + public static void SetRestartAdminSndMessageCallback(IPCMessageCallback implementation) { SndRestartAsAdminMsgCallback = implementation; } @@ -110,17 +110,17 @@ namespace Microsoft.PowerToys.Settings.UI.Views /// Set check for updates IPC callback function. /// /// delegate function implementation. - public void SetCheckForUpdatesMessageCallback(IPCMessageCallback implementation) + public static void SetCheckForUpdatesMessageCallback(IPCMessageCallback implementation) { CheckForUpdatesMsgCallback = implementation; } - public void SetElevationStatus(bool isElevated) + public static void SetElevationStatus(bool isElevated) { IsElevated = isElevated; } - public void SetIsUserAnAdmin(bool isAdmin) + public static void SetIsUserAnAdmin(bool isAdmin) { IsUserAnAdmin = isAdmin; } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/ShortcutGuidePage.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Views/ShortcutGuidePage.xaml index d3fd9a704b..5aa3825671 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ShortcutGuidePage.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ShortcutGuidePage.xaml @@ -9,7 +9,8 @@ xmlns:CustomControls="using:Microsoft.PowerToys.Settings.UI.Controls" xmlns:muxc="using:Microsoft.UI.Xaml.Controls" xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters" mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" + AutomationProperties.LandmarkType="Main"> 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 503b31d8d7..b1ec61624b 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ShortcutGuidePage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ShortcutGuidePage.xaml.cs @@ -17,7 +17,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views { InitializeComponent(); - var settingsUtils = new SettingsUtils(new SystemIOProvider()); + var settingsUtils = new SettingsUtils(); ViewModel = new ShortcutGuideViewModel(SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/VisibleIfNotEmpty.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/VisibleIfNotEmpty.cs index a99664ee37..f1b1846950 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/VisibleIfNotEmpty.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/VisibleIfNotEmpty.cs @@ -13,7 +13,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views { public object Convert(object value, Type targetType, object parameter, string language) { - return (value as IList).Count == 0 ? Visibility.Collapsed : Visibility.Visible; + return (value == null) || (value as IList).Count == 0 ? Visibility.Collapsed : Visibility.Visible; } public object ConvertBack(object value, Type targetType, object parameter, string language) diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/cs/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/cs/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index 04bddd934b..645ec990f9 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/cs/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/cs/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -145,6 +145,15 @@ + + + + + + + + + @@ -586,33 +595,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +730,15 @@ + + + + + + + + + @@ -1702,6 +1693,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1749,13 @@ - + - + + + + @@ -1882,15 +1903,6 @@ - - - - - - - - - @@ -1909,11 +1921,11 @@ - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/de/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/de/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index fc0e36b767..23bbafd8e9 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/de/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/de/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -145,6 +145,15 @@ + + + + + + + + + @@ -586,33 +595,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +730,15 @@ + + + + + + + + + @@ -1702,6 +1693,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1749,13 @@ - + - + + + + @@ -1882,15 +1903,6 @@ - - - - - - - - - @@ -1909,11 +1921,11 @@ - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/es/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/es/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index 4d155ec7f9..76942ed249 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/es/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/es/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -145,6 +145,15 @@ + + + + + + + + + @@ -586,33 +595,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +730,15 @@ + + + + + + + + + @@ -1702,6 +1693,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1749,13 @@ - + - + + + + @@ -1882,15 +1903,6 @@ - - - - - - - - - @@ -1909,11 +1921,11 @@ - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/fr/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/fr/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index 9942579f7f..5c2c8ff25b 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/fr/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/fr/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -145,6 +145,15 @@ + + + + + + + + + @@ -586,33 +595,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +730,15 @@ + + + + + + + + + @@ -1702,6 +1693,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1749,13 @@ - + - + + + + @@ -1882,15 +1903,6 @@ - - - - - - - - - @@ -1909,11 +1921,11 @@ - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/hu/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/hu/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index d03c54b940..11a3d1d736 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/hu/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/hu/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -109,24 +109,6 @@ - - - - - - - - - - - - - - - - - - @@ -145,6 +127,15 @@ + + + + + + + + + @@ -176,7 +167,7 @@ - + @@ -586,33 +577,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +712,15 @@ + + + + + + + + + @@ -1702,6 +1675,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1731,13 @@ - + - + + + + @@ -1792,15 +1795,6 @@ - - - - - - - - - @@ -1882,15 +1876,6 @@ - - - - - - - - - @@ -1909,11 +1894,11 @@ - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/it/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/it/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index 88a868e2c7..4124d85adb 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/it/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/it/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -145,6 +145,15 @@ + + + + + + + + + @@ -586,33 +595,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +730,15 @@ + + + + + + + + + @@ -1495,6 +1486,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1585,6 +1603,15 @@ + + + + + + + + + @@ -1702,6 +1729,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1785,13 @@ - + - + + + + @@ -1882,15 +1939,6 @@ - - - - - - - - - @@ -1909,11 +1957,11 @@ - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/ja/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/ja/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index f8ab54440c..03966f7659 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/ja/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/ja/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -145,6 +145,15 @@ + + + + + + + + + @@ -586,33 +595,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +730,15 @@ + + + + + + + + + @@ -1702,6 +1693,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1749,13 @@ - + + + + @@ -1882,15 +1903,6 @@ - - - - - - - - - @@ -1909,11 +1921,11 @@ - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/ko/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/ko/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index a5ddf1b730..bd437fb3bf 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/ko/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/ko/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -109,24 +109,6 @@ - - - - - - - - - - - - - - - - - - @@ -145,6 +127,15 @@ + + + + + + + + + @@ -176,7 +167,7 @@ - + @@ -586,33 +577,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +712,15 @@ + + + + + + + + + @@ -977,7 +950,7 @@ - + @@ -1094,7 +1067,7 @@ - + @@ -1702,6 +1675,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1731,13 @@ - + + + + @@ -1792,15 +1795,6 @@ - - - - - - - - - @@ -1882,15 +1876,6 @@ - - - - - - - - - @@ -1909,11 +1894,11 @@ - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/nl/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/nl/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index d844760a62..7124408af1 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/nl/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/nl/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -14,7 +14,7 @@ - + @@ -50,7 +50,7 @@ - + @@ -86,7 +86,7 @@ - + @@ -109,24 +109,6 @@ - - - - - - - - - - - - - - - - - - @@ -145,11 +127,20 @@ + + + + + + + + + - + @@ -185,7 +176,7 @@ - + @@ -194,7 +185,7 @@ - + @@ -586,33 +577,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +712,15 @@ + + + + + + + + + @@ -1112,7 +1085,7 @@ - + @@ -1220,7 +1193,7 @@ - + @@ -1229,7 +1202,7 @@ - + @@ -1702,6 +1675,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1731,13 @@ - + - + + + + @@ -1751,7 +1754,7 @@ - + @@ -1787,16 +1790,7 @@ - - - - - - - - - - + @@ -1832,7 +1826,7 @@ - + @@ -1859,7 +1853,7 @@ - + @@ -1882,15 +1876,6 @@ - - - - - - - - - @@ -1904,16 +1889,16 @@ - + - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/pl/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/pl/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index fd1426833b..5235d57eb1 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/pl/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/pl/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -109,24 +109,6 @@ - - - - - - - - - - - - - - - - - - @@ -145,6 +127,15 @@ + + + + + + + + + @@ -586,33 +577,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +712,15 @@ + + + + + + + + + @@ -1211,7 +1184,7 @@ - + @@ -1247,7 +1220,7 @@ - + @@ -1256,7 +1229,7 @@ - + @@ -1265,7 +1238,7 @@ - + @@ -1274,7 +1247,7 @@ - + @@ -1283,7 +1256,7 @@ - + @@ -1292,7 +1265,7 @@ - + @@ -1301,7 +1274,7 @@ - + @@ -1310,7 +1283,7 @@ - + @@ -1319,7 +1292,7 @@ - + @@ -1702,11 +1675,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -1715,7 +1715,7 @@ - + @@ -1731,10 +1731,13 @@ - + - + + + + @@ -1792,15 +1795,6 @@ - - - - - - - - - @@ -1882,15 +1876,6 @@ - - - - - - - - - @@ -1909,11 +1894,11 @@ - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/pt-BR/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/pt-BR/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index 129d7de1bf..a4bacf2600 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/pt-BR/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/pt-BR/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -145,6 +145,15 @@ + + + + + + + + + @@ -586,33 +595,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +730,15 @@ + + + + + + + + + @@ -1702,6 +1693,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1749,13 @@ - + - + + + + @@ -1882,15 +1903,6 @@ - - - - - - - - - @@ -1909,11 +1921,11 @@ - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/pt-PT/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/pt-PT/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index 1f1f2ef4bb..6ab63625ac 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/pt-PT/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/pt-PT/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -145,6 +145,15 @@ + + + + + + + + + @@ -586,33 +595,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +730,15 @@ + + + + + + + + + @@ -1702,6 +1693,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1749,13 @@ - + - + + + + @@ -1882,15 +1903,6 @@ - - - - - - - - - @@ -1909,11 +1921,11 @@ - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/ru/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/ru/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index 4d4719db25..1441f1d400 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/ru/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/ru/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -145,6 +145,15 @@ + + + + + + + + + @@ -586,33 +595,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +730,15 @@ + + + + + + + + + @@ -1702,6 +1693,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1749,13 @@ - + - + + + + @@ -1882,15 +1903,6 @@ - - - - - - - - - @@ -1909,11 +1921,11 @@ - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/sv/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/sv/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index 5815b90b28..9eeb7fc5db 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/sv/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/sv/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -145,6 +145,15 @@ + + + + + + + + + @@ -586,33 +595,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +730,15 @@ + + + + + + + + + @@ -1702,6 +1693,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1749,13 @@ - + - + + + + @@ -1882,15 +1903,6 @@ - - - - - - - - - @@ -1909,11 +1921,11 @@ - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/tr/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/tr/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index fdfe13e3e7..c8ff36ebf0 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/tr/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/tr/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -145,6 +145,15 @@ + + + + + + + + + @@ -586,33 +595,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +730,15 @@ + + + + + + + + + @@ -1702,6 +1693,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1749,13 @@ - + - + + + + @@ -1882,15 +1903,6 @@ - - - - - - - - - @@ -1909,11 +1921,11 @@ - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/zh-Hans/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/zh-Hans/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index dd2f9f54ac..3fd8808c47 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/zh-Hans/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/zh-Hans/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -145,6 +145,15 @@ + + + + + + + + + @@ -586,33 +595,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +730,15 @@ + + + + + + + + + @@ -1702,6 +1693,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1749,13 @@ - + - + + + + @@ -1882,15 +1903,6 @@ - - - - - - - - - @@ -1909,11 +1921,11 @@ - + - + - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/loc/zh-Hant/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl b/src/core/Microsoft.PowerToys.Settings.UI/loc/zh-Hant/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl index 75cc42ff01..865840e1c6 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/loc/zh-Hant/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl +++ b/src/core/Microsoft.PowerToys.Settings.UI/loc/zh-Hant/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw.lcl @@ -14,7 +14,7 @@ - + @@ -41,7 +41,7 @@ - + @@ -86,7 +86,7 @@ - + @@ -109,24 +109,6 @@ - - - - - - - - - - - - - - - - - - @@ -145,11 +127,20 @@ + + + + + + + + + - + @@ -185,7 +176,7 @@ - + @@ -194,7 +185,7 @@ - + @@ -586,33 +577,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -748,6 +712,15 @@ + + + + + + + + + @@ -869,7 +842,7 @@ - + @@ -1049,7 +1022,7 @@ - + @@ -1634,7 +1607,7 @@ - + @@ -1702,6 +1675,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1731,10 +1731,13 @@ - + - + + + + @@ -1751,7 +1754,7 @@ - + @@ -1778,7 +1781,7 @@ - + @@ -1792,15 +1795,6 @@ - - - - - - - - - @@ -1823,7 +1817,7 @@ - + @@ -1832,7 +1826,7 @@ - + @@ -1859,7 +1853,7 @@ - + @@ -1882,15 +1876,6 @@ - - - - - - - - - @@ -1904,16 +1889,16 @@ - + - + - + - + diff --git a/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj b/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj index 3eef221bc0..9126afe98a 100644 --- a/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj +++ b/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj @@ -225,6 +225,9 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + + 12.2.5 + 2.0.20525 diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/ColorRepresentationHelper.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/ColorRepresentationHelper.cs index 9472322986..f3aed7884d 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/ColorRepresentationHelper.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/ColorRepresentationHelper.cs @@ -66,6 +66,7 @@ namespace ColorPicker.Helpers saturation = Math.Round(saturation * 100); lightness = Math.Round(lightness * 100); + // Using InvariantCulture since this is used for color representation return $"hsl({hue.ToString(CultureInfo.InvariantCulture)}" + $", {saturation.ToString(CultureInfo.InvariantCulture)}%" + $", {lightness.ToString(CultureInfo.InvariantCulture)}%)"; @@ -84,6 +85,7 @@ namespace ColorPicker.Helpers saturation = Math.Round(saturation * 100); value = Math.Round(value * 100); + // Using InvariantCulture since this is used for color representation return $"hsv({hue.ToString(CultureInfo.InvariantCulture)}" + $", {saturation.ToString(CultureInfo.InvariantCulture)}%" + $", {value.ToString(CultureInfo.InvariantCulture)}%)"; @@ -103,6 +105,7 @@ namespace ColorPicker.Helpers yellow = Math.Round(yellow * 100); blackKey = Math.Round(blackKey * 100); + // Using InvariantCulture since this is used for color representation return $"cmyk({cyan.ToString(CultureInfo.InvariantCulture)}%" + $", {magenta.ToString(CultureInfo.InvariantCulture)}%" + $", {yellow.ToString(CultureInfo.InvariantCulture)}%" diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/Logger.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/Logger.cs index 7058a8ff51..56a149eb7d 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/Logger.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/Logger.cs @@ -5,22 +5,24 @@ using System; using System.Diagnostics; using System.Globalization; -using System.IO; +using System.IO.Abstractions; namespace ColorPicker.Helpers { public static class Logger { - private static readonly string ApplicationLogPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ColorPicker"); + private static readonly IFileSystem _fileSystem = new FileSystem(); + private static readonly string ApplicationLogPath = _fileSystem.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ColorPicker"); static Logger() { - if (!Directory.Exists(ApplicationLogPath)) + if (!_fileSystem.Directory.Exists(ApplicationLogPath)) { - Directory.CreateDirectory(ApplicationLogPath); + _fileSystem.Directory.CreateDirectory(ApplicationLogPath); } - var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.CurrentCulture) + ".txt"); + // Using InvariantCulture since this is used for a log file name + var logFilePath = _fileSystem.Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); diff --git a/src/modules/colorPicker/ColorPickerUI/Mouse/CursorManager.cs b/src/modules/colorPicker/ColorPickerUI/Mouse/CursorManager.cs index a523f575a0..7166f881d9 100644 --- a/src/modules/colorPicker/ColorPickerUI/Mouse/CursorManager.cs +++ b/src/modules/colorPicker/ColorPickerUI/Mouse/CursorManager.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.IO; +using System.IO.Abstractions; using ColorPicker.Helpers; using Microsoft.Win32; @@ -28,11 +28,13 @@ namespace ColorPicker.Mouse [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Interop object")] private const int SPIF_SENDCHANGE = 0x02; + private static readonly IFileSystem _fileSystem = new FileSystem(); + public static void SetColorPickerCursor() { BackupOriginalCursors(); - var colorPickerCursorPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ColorPickerCursorName); + var colorPickerCursorPath = _fileSystem.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ColorPickerCursorName); ChangeCursor(colorPickerCursorPath, ArrowRegistryName); ChangeCursor(colorPickerCursorPath, IBeamRegistryName); } diff --git a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs index a1e50f5ec7..3ccea1f1e4 100644 --- a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs +++ b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs @@ -5,6 +5,7 @@ using System; using System.ComponentModel.Composition; using System.IO; +using System.IO.Abstractions; using System.Threading; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; @@ -20,14 +21,14 @@ namespace ColorPicker.Settings private const int MaxNumberOfRetry = 5; [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Actually, call back is LoadSettingsFromJson")] - private readonly FileSystemWatcher _watcher; + private readonly IFileSystemWatcher _watcher; private readonly object _loadingSettingsLock = new object(); [ImportingConstructor] public UserSettings() { - _settingsUtils = new SettingsUtils(new SystemIOProvider()); + _settingsUtils = new SettingsUtils(); ChangeCursor = new SettingItem(true); ActivationShortcut = new SettingItem(DefaultActivationShortcut); CopiedColorRepresentation = new SettingItem(ColorRepresentationType.HEX); diff --git a/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorHelperTest.cs b/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorHelperTest.cs index 5f9c867b93..aae56ac7c2 100644 --- a/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorHelperTest.cs +++ b/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorHelperTest.cs @@ -167,7 +167,7 @@ namespace UnitTest_ColorPickerUI.Helpers { var color = Color.FromArgb(red, green, blue); - Exception? exception = null; + Exception exception = null; try { diff --git a/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj b/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj index c3c3256613..48f90e048e 100644 --- a/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj +++ b/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj @@ -2,20 +2,16 @@ netcoreapp3.1 - UnitTest_ColorPickerUI false - enable - 8.0 - Library + x64 + true - x64 ..\..\..\..\x64\Debug\modules\ColorPicker\UnitTest-ColorPickerUI\ - x64 ..\..\..\..\x64\Release\modules\ColorPicker\UnitTest-ColorPickerUI\ diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs index 7b6d389c4c..751fc6c40a 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.IO.Abstractions; using System.Linq; using System.Runtime.InteropServices; using System.Text; @@ -41,6 +42,8 @@ namespace FancyZonesEditor private const string CrashReportDynamicAssemblyTag = "dynamic assembly doesn't have location"; private const string CrashReportLocationNullTag = "location is null or empty"; + private readonly IFileSystem _fileSystem = new FileSystem(); + public Settings ZoneSettings { get; } public App() @@ -157,6 +160,8 @@ namespace FancyZonesEditor sb.AppendLine("## " + CrashReportEnvironmentTag); sb.AppendLine(CrashReportCommandLineTag + Environment.CommandLine); + + // Using InvariantCulture since this is used for a timestamp internally sb.AppendLine(CrashReportTimestampTag + DateTime.Now.ToString(CultureInfo.InvariantCulture)); sb.AppendLine(CrashReportOSVersionTag + Environment.OSVersion.VersionString); sb.AppendLine(CrashReportIntPtrLengthTag + IntPtr.Size); diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml index 6940d45d68..7553a0ddcf 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml +++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml @@ -66,11 +66,11 @@ @@ -154,7 +154,7 @@ @@ -151,7 +151,7 @@ @@ -258,13 +258,37 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + +