Merge branch 'master' into spelling

This commit is contained in:
Clint Rutkas 2020-11-02 09:57:48 -08:00 committed by GitHub
commit a66b276e7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
245 changed files with 2177 additions and 1210 deletions

View File

@ -105,7 +105,7 @@ Download PowerToys from [WinGet](https://github.com/microsoft/winget-cli/release
WinGet install powertoys 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. 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.

View File

@ -64,4 +64,94 @@
<data name="DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" xml:space="preserve"> <data name="DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" xml:space="preserve">
<value>PowerToys installation error</value> <value>PowerToys installation error</value>
</data> </data>
<data name="GITHUB_NEW_VERSION_AVAILABLE" xml:space="preserve">
<value>An update to PowerToys is available.</value>
</data>
<data name="GITHUB_NEW_VERSION_DOWNLOAD_STARTED" xml:space="preserve">
<value>PowerToys download started.</value>
</data>
<data name="GITHUB_NEW_VERSION_READY_TO_INSTALL" xml:space="preserve">
<value>An update to PowerToys is ready to install.</value>
</data>
<data name="GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR" xml:space="preserve">
<value>Error: couldn't download PowerToys installer. Visit our GitHub page to update.</value>
</data>
<data name="GITHUB_NEW_VERSION_UPDATE_NOW" xml:space="preserve">
<value>Update now</value>
</data>
<data name="GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART" xml:space="preserve">
<value>At next launch</value>
</data>
<data name="UNINSTALLATION_UNKNOWN_ERROR" xml:space="preserve">
<value>Error: please uninstall the previous version of PowerToys manually.</value>
</data>
<data name="GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT" xml:space="preserve">
<value>An update to PowerToys is available. Visit our GitHub page to update.</value>
</data>
<data name="GITHUB_NEW_VERSION_UNAVAILABLE" xml:space="preserve">
<value>PowerToys is up to date.</value>
</data>
<data name="GITHUB_NEW_VERSION_VISIT" xml:space="preserve">
<value>Visit</value>
</data>
<data name="GITHUB_NEW_VERSION_MORE_INFO" xml:space="preserve">
<value>More info...</value>
</data>
<data name="GITHUB_NEW_VERSION_ABORT" xml:space="preserve">
<value>Abort</value>
</data>
<data name="GITHUB_NEW_VERSION_SNOOZE_TITLE" xml:space="preserve">
<value>Click Snooze to be reminded in:</value>
</data>
<data name="GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D" xml:space="preserve">
<value>1 day</value>
</data>
<data name="GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D" xml:space="preserve">
<value>5 days</value>
</data>
<data name="DOWNLOAD_IN_PROGRESS" xml:space="preserve">
<value>Downloading...</value>
</data>
<data name="DOWNLOAD_COMPLETE" xml:space="preserve">
<value>Download complete</value>
</data>
<data name="TOAST_TITLE" xml:space="preserve">
<value>PowerToys Update</value>
</data>
<data name="OFFER_UNINSTALL_MSI" xml:space="preserve">
<value>We've detected a previous installation of PowerToys. Would you like to remove it?</value>
</data>
<data name="OFFER_UNINSTALL_MSI_TITLE" xml:space="preserve">
<value>PowerToys: uninstall previous version?</value>
</data>
<data name="INSTALLER_EXTRACT_ERROR" xml:space="preserve">
<value>Couldn't extract MSI installer!</value>
</data>
<data name="EXTRACTING_INSTALLER" xml:space="preserve">
<value>Extracting PowerToys MSI...</value>
</data>
<data name="UNINSTALLING_PREVIOUS_VERSION" xml:space="preserve">
<value>Uninstalling previous PowerToys version...</value>
</data>
<data name="UNINSTALL_PREVIOUS_VERSION_ERROR" xml:space="preserve">
<value>Couldn't uninstall previous PowerToys version!</value>
</data>
<data name="INSTALLING_DOTNET" xml:space="preserve">
<value>Installing dotnet...</value>
</data>
<data name="DOTNET_INSTALL_ERROR" xml:space="preserve">
<value>Couldn't install dotnet!</value>
</data>
<data name="INSTALLING_NEW_VERSION" xml:space="preserve">
<value>Installing new PowerToys version...</value>
</data>
<data name="NEW_VERSION_INSTALLATION_DONE" xml:space="preserve">
<value>PowerToys installation complete!</value>
</data>
<data name="NEW_VERSION_INSTALLATION_ERROR" xml:space="preserve">
<value>Couldn't install new PowerToys version.</value>
</data>
<data name="SNOOZE_BUTTON" xml:space="preserve">
<value>Snooze</value>
</data>
</root> </root>

View File

@ -14,11 +14,14 @@
extern "C" IMAGE_DOS_HEADER __ImageBase; extern "C" IMAGE_DOS_HEADER __ImageBase;
auto Strings = updating::notifications::strings::create();
#define STR_HELPER(x) #x #define STR_HELPER(x) #x
#define STR(x) STR_HELPER(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 APPLICATION_ID[] = L"PowerToysInstaller";
const wchar_t INSTALLATION_TOAST_TITLE[] = L"PowerToys Installation";
const wchar_t TOAST_TAG[] = L"PowerToysInstallerProgress"; 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 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"; 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
#undef STR_HELPER #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; namespace fs = std::filesystem;
std::optional<fs::path> extractEmbeddedInstaller() std::optional<fs::path> extractEmbeddedInstaller()
@ -96,7 +85,6 @@ void setup_log(fs::path directory, const spdlog::level::level_enum severity)
int bootstrapper() int bootstrapper()
{ {
using namespace localized_strings;
winrt::init_apartment(); winrt::init_apartment();
cxxopts::Options options{ "PowerToysBootstrapper" }; cxxopts::Options options{ "PowerToysBootstrapper" };
// clang-format off // clang-format off
@ -252,7 +240,7 @@ int bootstrapper()
iconPath = std::move(*extractedIcon); iconPath = std::move(*extractedIcon);
} }
spdlog::debug("Registering app id for toast notifications"); 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([&] { auto removeShortcut = wil::scope_exit([&] {
notifications::unregister_application_id(); notifications::unregister_application_id();
@ -274,12 +262,12 @@ int bootstrapper()
std::mutex progressLock; std::mutex progressLock;
notifications::progress_bar_params progressParams; notifications::progress_bar_params progressParams;
progressParams.progress = 0.0f; 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) }; notifications::toast_params params{ TOAST_TAG, false, std::move(progressParams) };
if (!silent) if (!silent)
{ {
spdlog::debug("Launching progress toast notification"); 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([&] { auto processToasts = wil::scope_exit([&] {
@ -322,7 +310,7 @@ int bootstrapper()
{ {
if (!silent) 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()); spdlog::error("Couldn't install the MSI installer ({})", GetLastError());
return 1; return 1;
@ -332,7 +320,7 @@ int bootstrapper()
fs::remove(*installerPath, _); 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"); spdlog::debug("Acquiring existing MSI package path");
const auto package_path = updating::get_msi_package_path(); const auto package_path = updating::get_msi_package_path();
if (!package_path.empty()) if (!package_path.empty())
@ -343,15 +331,15 @@ int bootstrapper()
{ {
spdlog::debug("Existing MSI package path not found"); 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()); 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; const bool installDotnet = !skipDotnetInstall;
if (installDotnet) if (installDotnet)
{ {
updateProgressBar(.5f, INSTALLING_DOTNET); updateProgressBar(.5f, GET_RESOURCE_STRING(IDS_INSTALLING_DOTNET).c_str());
} }
try try
@ -365,7 +353,7 @@ int bootstrapper()
!updating::install_dotnet(silent) && !updating::install_dotnet(silent) &&
!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); 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 // Always skip dotnet install, because we should've installed it from here earlier
std::wstring msiProps = L"SKIPDOTNETINSTALL=1 "; std::wstring msiProps = L"SKIPDOTNETINSTALL=1 ";
spdlog::debug("Launching MSI installation for new package {}", installerPath->string()); spdlog::debug("Launching MSI installation for new package {}", installerPath->string());
const bool installationDone = MsiInstallProductW(installerPath->c_str(), msiProps.c_str()) == ERROR_SUCCESS; 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) if (!installationDone)
{ {
spdlog::error("Couldn't install new MSI package ({})", GetLastError()); spdlog::error("Couldn't install new MSI package ({})", GetLastError());

View File

@ -13,12 +13,18 @@
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val> <Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Nepovedlo se stáhnout modul .NET Core Desktop Runtime 3.1, nainstalujte ho prosím ručně.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys installation error]]></Val> <Val><![CDATA[PowerToys installation error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Chyba instalace PowerToys]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,12 +13,18 @@
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val> <Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[.NET Core Desktop Runtime 3.1 konnte nicht heruntergeladen werden. Führen Sie die Installation manuell durch.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys installation error]]></Val> <Val><![CDATA[PowerToys installation error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys-Installationsfehler]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,12 +13,18 @@
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val> <Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[No se pudo descargar .NET Core Desktop Runtime 3.1. Instálelo manualmente.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys installation error]]></Val> <Val><![CDATA[PowerToys installation error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Error de instalación de PowerToys]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,12 +13,18 @@
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val> <Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Impossible de télécharger .NET Core Desktop Runtime 3.1, installez-le manuellement.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys installation error]]></Val> <Val><![CDATA[PowerToys installation error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Erreur d'installation de PowerToys]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,12 +13,18 @@
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val> <Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Nem sikerült letölteni a .NET Core futásidejű asztali összetevő 3.1-es verzióját, telepítse manuálisan.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys installation error]]></Val> <Val><![CDATA[PowerToys installation error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys-telepítési hiba]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,12 +13,18 @@
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val> <Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[.NET Core デスクトップ ランタイム 3.1 をダウンロードできませんでした。手動でインストールしてください。]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys installation error]]></Val> <Val><![CDATA[PowerToys installation error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys のインストール エラー]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,12 +13,18 @@
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val> <Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Kan .NET Core Desktop Runtime 3.1 niet downloaden. Installeer de runtime handmatig.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys installation error]]></Val> <Val><![CDATA[PowerToys installation error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Installatiefout voor PowerToys]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,12 +13,18 @@
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val> <Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Não foi possível baixar o .NET Core Desktop Runtime 3.1, instale-o manualmente.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys installation error]]></Val> <Val><![CDATA[PowerToys installation error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Erro de instalação do PowerToys]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,12 +13,18 @@
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val> <Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Не удалось скачать среду выполнения .NET Core 3.1, установите ее вручную.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys installation error]]></Val> <Val><![CDATA[PowerToys installation error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Ошибка установки PowerToys]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,12 +13,18 @@
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val> <Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Det gick inte att ladda ned .NET Core Desktop Runtime 3.1. Installera det manuellt.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys installation error]]></Val> <Val><![CDATA[PowerToys installation error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Installationsfel för PowerToys]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,12 +13,18 @@
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val> <Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[.NET Core Desktop Çalışma Zamanı 3.1 indirilemedi, lütfen kendiniz yükleyin.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys installation error]]></Val> <Val><![CDATA[PowerToys installation error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys yükleme hatası]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,12 +13,18 @@
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val> <Val><![CDATA[Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[无法下载 .NET Core 桌面运行时 3.1。请手动安装它。]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys installation error]]></Val> <Val><![CDATA[PowerToys installation error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys 安装错误]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -883,7 +883,7 @@
<ComponentGroup Id="LauncherComponents"> <ComponentGroup Id="LauncherComponents">
<Component Id="launcherInstallComponent" Directory="LauncherInstallFolder" Guid="5E688DB4-C522-4268-BA54-ED1CDFFE9DB6"> <Component Id="launcherInstallComponent" Directory="LauncherInstallFolder" Guid="5E688DB4-C522-4268-BA54-ED1CDFFE9DB6">
<File Source="$(var.BinX64Dir)modules\Launcher\Microsoft.Launcher.dll" /> <File Source="$(var.BinX64Dir)modules\Launcher\Microsoft.Launcher.dll" />
<?foreach File in concrt140_app.dll;ICSharpCode.SharpZipLib.dll;JetBrains.Annotations.dll;Mages.Core.dll;Microsoft.Search.Interop.dll;EntityFramework.SqlServer.dll;EntityFramework.dll;Mono.Cecil.dll;Mono.Cecil.Mdb.dll;Mono.Cecil.Pdb.dll;Mono.Cecil.Rocks.dll;msvcp140_1_app.dll;msvcp140_2_app.dll;msvcp140_app.dll;Newtonsoft.Json.dll;NLog.dll;NLog.Extensions.Logging.dll;PowerLauncher.deps.json;PowerLauncher.dll;PowerLauncher.exe;Microsoft.Xaml.Behaviors.dll;System.Text.Json.dll;sni.dll;System.Data.SQLite.EF6.dll;PowerLauncher.runtimeconfig.json;SQLite.Interop.dll;System.Data.OleDb.dll;System.Data.SqlClient.dll;System.Data.SQLite.dll;vcamp140_app.dll;vccorlib140_app.dll;vcomp140_app.dll;vcruntime140_1_app.dll;vcruntime140_app.dll;Wox.Core.dll;Wox.Infrastructure.dll;Wox.Plugin.dll;PowerToysInterop.dll;Telemetry.dll;PowerLauncher.Telemetry.dll;Microsoft.Extensions.Configuration.Abstractions.dll;Microsoft.Extensions.Configuration.Binder.dll;Microsoft.Extensions.Configuration.dll;Microsoft.Extensions.DependencyInjection.Abstractions.dll;Microsoft.Extensions.DependencyInjection.dll;Microsoft.Extensions.Logging.Abstractions.dll;Microsoft.Extensions.Logging.dll;Microsoft.Extensions.Options.dll;Microsoft.Extensions.Primitives.dll;ControlzEx.dll;MahApps.Metro.dll;ManagedCommon.dll?> <?foreach File in concrt140_app.dll;ICSharpCode.SharpZipLib.dll;JetBrains.Annotations.dll;Mages.Core.dll;Microsoft.Search.Interop.dll;EntityFramework.SqlServer.dll;EntityFramework.dll;Mono.Cecil.dll;Mono.Cecil.Mdb.dll;Mono.Cecil.Pdb.dll;Mono.Cecil.Rocks.dll;msvcp140_1_app.dll;msvcp140_2_app.dll;msvcp140_app.dll;Newtonsoft.Json.dll;NLog.dll;NLog.Extensions.Logging.dll;PowerLauncher.deps.json;PowerLauncher.dll;PowerLauncher.exe;Microsoft.Xaml.Behaviors.dll;System.Text.Json.dll;sni.dll;System.Data.SQLite.EF6.dll;PowerLauncher.runtimeconfig.json;SQLite.Interop.dll;System.Data.OleDb.dll;System.Data.SqlClient.dll;System.Data.SQLite.dll;vcamp140_app.dll;vccorlib140_app.dll;vcomp140_app.dll;vcruntime140_1_app.dll;vcruntime140_app.dll;Wox.Core.dll;Wox.Infrastructure.dll;Wox.Plugin.dll;PowerToysInterop.dll;Telemetry.dll;PowerLauncher.Telemetry.dll;Microsoft.Extensions.Configuration.Abstractions.dll;Microsoft.Extensions.Configuration.Binder.dll;Microsoft.Extensions.Configuration.dll;Microsoft.Extensions.DependencyInjection.Abstractions.dll;Microsoft.Extensions.DependencyInjection.dll;Microsoft.Extensions.Logging.Abstractions.dll;Microsoft.Extensions.Logging.dll;Microsoft.Extensions.Options.dll;Microsoft.Extensions.Primitives.dll;ControlzEx.dll;ManagedCommon.dll?>
<File Id="File_$(var.File)" Source="$(var.BinX64Dir)modules\launcher\$(var.File)" /> <File Id="File_$(var.File)" Source="$(var.BinX64Dir)modules\launcher\$(var.File)" />
<?endforeach?> <?endforeach?>
<File Source="$(var.BinX64Dir)SettingsUIRunner\Microsoft.PowerToys.Settings.UI.Lib.dll" /> <File Source="$(var.BinX64Dir)SettingsUIRunner\Microsoft.PowerToys.Settings.UI.Lib.dll" />

View File

@ -64,4 +64,67 @@
<data name="DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" xml:space="preserve"> <data name="DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" xml:space="preserve">
<value>PowerToys installation error</value> <value>PowerToys installation error</value>
</data> </data>
<data name="GITHUB_NEW_VERSION_AVAILABLE" xml:space="preserve">
<value>An update to PowerToys is available.</value>
</data>
<data name="GITHUB_NEW_VERSION_DOWNLOAD_STARTED" xml:space="preserve">
<value>PowerToys download started.</value>
</data>
<data name="GITHUB_NEW_VERSION_READY_TO_INSTALL" xml:space="preserve">
<value>An update to PowerToys is ready to install.</value>
</data>
<data name="GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR" xml:space="preserve">
<value>Error: couldn't download PowerToys installer. Visit our GitHub page to update.</value>
</data>
<data name="GITHUB_NEW_VERSION_UPDATE_NOW" xml:space="preserve">
<value>Update now</value>
</data>
<data name="GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART" xml:space="preserve">
<value>At next launch</value>
</data>
<data name="UNINSTALLATION_UNKNOWN_ERROR" xml:space="preserve">
<value>Error: please uninstall the previous version of PowerToys manually.</value>
</data>
<data name="GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT" xml:space="preserve">
<value>An update to PowerToys is available. Visit our GitHub page to update.</value>
</data>
<data name="GITHUB_NEW_VERSION_UNAVAILABLE" xml:space="preserve">
<value>PowerToys is up to date.</value>
</data>
<data name="GITHUB_NEW_VERSION_VISIT" xml:space="preserve">
<value>Visit</value>
</data>
<data name="GITHUB_NEW_VERSION_MORE_INFO" xml:space="preserve">
<value>More info...</value>
</data>
<data name="GITHUB_NEW_VERSION_ABORT" xml:space="preserve">
<value>Abort</value>
</data>
<data name="GITHUB_NEW_VERSION_SNOOZE_TITLE" xml:space="preserve">
<value>Click Snooze to be reminded in:</value>
</data>
<data name="GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D" xml:space="preserve">
<value>1 day</value>
</data>
<data name="GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D" xml:space="preserve">
<value>5 days</value>
</data>
<data name="DOWNLOAD_IN_PROGRESS" xml:space="preserve">
<value>Downloading...</value>
</data>
<data name="DOWNLOAD_COMPLETE" xml:space="preserve">
<value>Download complete</value>
</data>
<data name="TOAST_TITLE" xml:space="preserve">
<value>PowerToys Update</value>
</data>
<data name="OFFER_UNINSTALL_MSI" xml:space="preserve">
<value>We've detected a previous installation of PowerToys. Would you like to remove it?</value>
</data>
<data name="OFFER_UNINSTALL_MSI_TITLE" xml:space="preserve">
<value>PowerToys: uninstall previous version?</value>
</data>
<data name="SNOOZE_BUTTON" xml:space="preserve">
<value>Snooze</value>
</data>
</root> </root>

View File

@ -1,4 +1,6 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include "Generated Files/resource.h"
#include <Windows.h> #include <Windows.h>
#include <shellapi.h> #include <shellapi.h>
@ -16,10 +18,10 @@
#include "../runner/tray_icon.h" #include "../runner/tray_icon.h"
#include "../runner/action_runner_utils.h" #include "../runner/action_runner_utils.h"
#include "Generated Files/resource.h"
extern "C" IMAGE_DOS_HEADER __ImageBase; extern "C" IMAGE_DOS_HEADER __ImageBase;
auto Strings = updating::notifications::strings::create();
int uninstall_msi_action() int uninstall_msi_action()
{ {
const auto package_path = updating::get_msi_package_path(); const auto package_path = updating::get_msi_package_path();
@ -27,7 +29,7 @@ int uninstall_msi_action()
{ {
return 0; return 0;
} }
if (!updating::uninstall_msi_version(package_path)) if (!updating::uninstall_msi_version(package_path, Strings))
{ {
return -1; return -1;
} }

View File

@ -13,30 +13,45 @@
<Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys is already running.]]></Val> <Val><![CDATA[PowerToys is already running.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Sada PowerToys je už spuštěná.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys was successfully updated!]]></Val> <Val><![CDATA[PowerToys was successfully updated!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Sada PowerToys se úspěšně aktualizovala.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as an administrator!]]></Val> <Val><![CDATA[Could not start PowerToys as an administrator!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Nepovedlo se spustit PowerToys jako správce.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as a user!]]></Val> <Val><![CDATA[Could not start PowerToys as a user!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Nepovedlo se spustit PowerToys jako uživatel.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys Error]]></Val> <Val><![CDATA[PowerToys Error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Chyba PowerToys]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,30 +13,45 @@
<Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys is already running.]]></Val> <Val><![CDATA[PowerToys is already running.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys wird bereits ausgeführt.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys was successfully updated!]]></Val> <Val><![CDATA[PowerToys was successfully updated!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys wurde erfolgreich aktualisiert.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as an administrator!]]></Val> <Val><![CDATA[Could not start PowerToys as an administrator!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys konnte nicht als Administrator gestartet werden.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as a user!]]></Val> <Val><![CDATA[Could not start PowerToys as a user!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys konnte nicht als Benutzer gestartet werden.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys Error]]></Val> <Val><![CDATA[PowerToys Error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys-Fehler]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,30 +13,45 @@
<Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys is already running.]]></Val> <Val><![CDATA[PowerToys is already running.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys ya está en ejecución.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys was successfully updated!]]></Val> <Val><![CDATA[PowerToys was successfully updated!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys se actualizó correctamente.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as an administrator!]]></Val> <Val><![CDATA[Could not start PowerToys as an administrator!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[No se pudo iniciar PowerToys como administrador.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as a user!]]></Val> <Val><![CDATA[Could not start PowerToys as a user!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[No se pudo iniciar PowerToys como usuario.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys Error]]></Val> <Val><![CDATA[PowerToys Error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Error de PowerToys]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,30 +13,45 @@
<Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys is already running.]]></Val> <Val><![CDATA[PowerToys is already running.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys est déjà en cours d'exécution.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys was successfully updated!]]></Val> <Val><![CDATA[PowerToys was successfully updated!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys a été mis à jour !]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as an administrator!]]></Val> <Val><![CDATA[Could not start PowerToys as an administrator!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Impossible de démarrer PowerToys comme administrateur !]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as a user!]]></Val> <Val><![CDATA[Could not start PowerToys as a user!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Impossible de démarrer PowerToys comme utilisateur !]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys Error]]></Val> <Val><![CDATA[PowerToys Error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Erreur PowerToys]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,30 +13,45 @@
<Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys is already running.]]></Val> <Val><![CDATA[PowerToys is already running.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[A PowerToys már fut.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys was successfully updated!]]></Val> <Val><![CDATA[PowerToys was successfully updated!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[A PowerToys frissítése sikerült.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as an administrator!]]></Val> <Val><![CDATA[Could not start PowerToys as an administrator!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[A PowerToys rendszergazdaként való indítása sikertelen.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as a user!]]></Val> <Val><![CDATA[Could not start PowerToys as a user!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[A PowerToys felhasználóként való indítása sikertelen.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys Error]]></Val> <Val><![CDATA[PowerToys Error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys-hiba]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,30 +13,45 @@
<Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys is already running.]]></Val> <Val><![CDATA[PowerToys is already running.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys は既に実行中です。]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys was successfully updated!]]></Val> <Val><![CDATA[PowerToys was successfully updated!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys が正常に更新されました。]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as an administrator!]]></Val> <Val><![CDATA[Could not start PowerToys as an administrator!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys を管理者として開始できませんでした。]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as a user!]]></Val> <Val><![CDATA[Could not start PowerToys as a user!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys をユーザーとして開始できませんでした。]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys Error]]></Val> <Val><![CDATA[PowerToys Error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys エラー]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,30 +13,45 @@
<Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys is already running.]]></Val> <Val><![CDATA[PowerToys is already running.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys is al actief.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys was successfully updated!]]></Val> <Val><![CDATA[PowerToys was successfully updated!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys is bijgewerkt.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as an administrator!]]></Val> <Val><![CDATA[Could not start PowerToys as an administrator!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Kan PowerToys niet starten als een beheerder.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as a user!]]></Val> <Val><![CDATA[Could not start PowerToys as a user!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Kan PowerToys niet starten als een gebruiker.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys Error]]></Val> <Val><![CDATA[PowerToys Error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys-fout]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,30 +13,45 @@
<Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys is already running.]]></Val> <Val><![CDATA[PowerToys is already running.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[O PowerToys já está em execução.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys was successfully updated!]]></Val> <Val><![CDATA[PowerToys was successfully updated!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[O PowerToys foi atualizado com êxito!]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as an administrator!]]></Val> <Val><![CDATA[Could not start PowerToys as an administrator!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Não foi possível iniciar o PowerToys como um administrador!]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as a user!]]></Val> <Val><![CDATA[Could not start PowerToys as a user!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Não foi possível iniciar o PowerToys como um usuário!]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys Error]]></Val> <Val><![CDATA[PowerToys Error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Erro do PowerToys]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,30 +13,45 @@
<Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys is already running.]]></Val> <Val><![CDATA[PowerToys is already running.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys уже выполняется.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys was successfully updated!]]></Val> <Val><![CDATA[PowerToys was successfully updated!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Обновление PowerToys выполнено.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as an administrator!]]></Val> <Val><![CDATA[Could not start PowerToys as an administrator!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Не удалось запустить PowerToys от имени администратора.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as a user!]]></Val> <Val><![CDATA[Could not start PowerToys as a user!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Не удалось запустить PowerToys от имени пользователя.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys Error]]></Val> <Val><![CDATA[PowerToys Error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Ошибка PowerToys]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,30 +13,45 @@
<Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys is already running.]]></Val> <Val><![CDATA[PowerToys is already running.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys zaten çalışıyor.]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys was successfully updated!]]></Val> <Val><![CDATA[PowerToys was successfully updated!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys başarıyla güncelleştirildi!]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as an administrator!]]></Val> <Val><![CDATA[Could not start PowerToys as an administrator!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys yönetici olarak başlatılamadı!]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as a user!]]></Val> <Val><![CDATA[Could not start PowerToys as a user!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys kullanıcı olarak başlatılamadı!]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys Error]]></Val> <Val><![CDATA[PowerToys Error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys Hatası]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -13,30 +13,45 @@
<Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ANOTHER_INSTANCE_RUNNING" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys is already running.]]></Val> <Val><![CDATA[PowerToys is already running.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys 已在运行。]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";AUTOUPDATE_SUCCESS" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys was successfully updated!]]></Val> <Val><![CDATA[PowerToys was successfully updated!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[已成功更新 PowerToys!]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_ELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as an administrator!]]></Val> <Val><![CDATA[Could not start PowerToys as an administrator!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[无法以管理员身份启动 PowerToys!]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";COULDNOT_RESTART_NONELEVATED" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Could not start PowerToys as a user!]]></Val> <Val><![CDATA[Could not start PowerToys as a user!]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[无法以用户身份启动 PowerToys!]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ERROR" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys Error]]></Val> <Val><![CDATA[PowerToys Error]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[PowerToys 错误]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>

View File

@ -85,22 +85,22 @@ std::optional<std::wstring> get_last_error_message(const DWORD dw)
return message; 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); const auto system_message = get_last_error_message(dw);
if (!system_message.has_value()) if (!system_message.has_value())
{ {
return; 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) if (lpDisplayBuf != NULL)
{ {
StringCchPrintfW(lpDisplayBuf, StringCchPrintfW(lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(WCHAR), LocalSize(lpDisplayBuf) / sizeof(WCHAR),
localized_strings::LAST_ERROR_FORMAT_STRING, L"%s: %s (%d)",
lpszFunction, functionName,
dw, system_message->c_str(),
system_message->c_str()); dw);
MessageBoxW(NULL, (LPCTSTR)lpDisplayBuf, errorTitle, MB_OK | MB_ICONERROR); MessageBoxW(NULL, (LPCTSTR)lpDisplayBuf, errorTitle, MB_OK | MB_ICONERROR);
LocalFree(lpDisplayBuf); LocalFree(lpDisplayBuf);
} }

View File

@ -7,12 +7,6 @@
#include <vector> #include <vector>
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. // Gets position of given window.
std::optional<RECT> get_window_pos(HWND hwnd); std::optional<RECT> 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<uint32_t> timeout_seconds = {}); int run_message_loop(const bool until_idle = false, const std::optional<uint32_t> timeout_seconds = {});
std::optional<std::wstring> get_last_error_message(const DWORD dw); std::optional<std::wstring> 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 enum WindowState
{ {

View File

@ -33,8 +33,7 @@ using winrt::Windows::UI::Notifications::ToastNotificationManager;
namespace fs = std::filesystem; namespace fs = std::filesystem;
// This namespace contains strings that SHOULD NOT be localized namespace // Strings in this namespace should not be localized
namespace
{ {
constexpr std::wstring_view TASK_NAME = L"PowerToysBackgroundNotificationsHandler"; constexpr std::wstring_view TASK_NAME = L"PowerToysBackgroundNotificationsHandler";
constexpr std::wstring_view TASK_ENTRYPOINT = L"PowerToysNotifications.BackgroundHandler"; constexpr std::wstring_view TASK_ENTRYPOINT = L"PowerToysNotifications.BackgroundHandler";
@ -45,11 +44,6 @@ namespace
constexpr std::wstring_view DEFAULT_TOAST_GROUP = L"PowerToysToastTag"; 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 DWORD loop_thread_id()
{ {
static const DWORD thread_id = GetCurrentThreadId(); static const DWORD thread_id = GetCurrentThreadId();
@ -365,7 +359,7 @@ void notifications::show_toast_with_activations(std::wstring message,
toast_xml += '"'; toast_xml += '"';
} }
toast_xml += LR"( content=")"; toast_xml += LR"( content=")";
toast_xml += localized_strings::SNOOZE_BUTTON; toast_xml += b.snooze_button_title;
toast_xml += LR"(" />)"; toast_xml += LR"(" />)";
} }, } },
actions[i]); actions[i]);

View File

@ -28,6 +28,7 @@ namespace notifications
{ {
std::wstring snooze_title; std::wstring snooze_title;
std::vector<snooze_duration> durations; std::vector<snooze_duration> durations;
std::wstring snooze_button_title;
}; };
struct link_button struct link_button
@ -45,7 +46,7 @@ namespace notifications
struct progress_bar_params struct progress_bar_params
{ {
std::wstring_view progress_title; std::wstring progress_title;
float progress = 0.f; float progress = 0.f;
}; };

View File

@ -9,39 +9,10 @@
#include "VersionHelper.h" #include "VersionHelper.h"
#include "version.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 updating
{ {
namespace notifications namespace notifications
{ {
using namespace localized_strings;
using namespace ::notifications; using namespace ::notifications;
std::wstring current_version_to_next_version(const updating::new_version_download_info& info) 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; return current_version_to_next_version;
} }
void show_unavailable() void show_unavailable(const notifications::strings& strings)
{ {
remove_toasts(UPDATING_PROCESS_TOAST_TAG); remove_toasts(UPDATING_PROCESS_TOAST_TAG);
toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false }; toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false };
std::wstring contents = GITHUB_NEW_VERSION_UNAVAILABLE; std::wstring contents = strings.GITHUB_NEW_VERSION_UNAVAILABLE;
show_toast(std::move(contents), TOAST_TITLE, std::move(toast_params)); 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); remove_toasts(UPDATING_PROCESS_TOAST_TAG);
toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false }; 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); contents += current_version_to_next_version(info);
show_toast_with_activations(std::move(contents), 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)); 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); remove_toasts(UPDATING_PROCESS_TOAST_TAG);
progress_bar_params progress_bar_params; progress_bar_params progress_bar_params;
std::wstring progress_title{ info.version_string }; std::wstring progress_title{ info.version_string };
progress_title += L' '; 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_title = progress_title;
progress_bar_params.progress = .0f; progress_bar_params.progress = .0f;
toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false, std::move(progress_bar_params) }; 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, show_toast_with_activations(strings.GITHUB_NEW_VERSION_DOWNLOAD_STARTED,
TOAST_TITLE, strings.TOAST_TITLE,
{}, {},
{}, {},
std::move(toast_params)); 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); remove_toasts(UPDATING_PROCESS_TOAST_TAG);
toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false }; 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); contents += current_version_to_next_version(info);
show_toast_with_activations(std::move(contents), 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)); 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); remove_toasts(UPDATING_PROCESS_TOAST_TAG);
toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false }; 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); contents += current_version_to_next_version(info);
show_toast_with_activations(std::move(contents), 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)); 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); remove_toasts(UPDATING_PROCESS_TOAST_TAG);
toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false }; 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); new_version_ready += current_version_to_next_version(info);
show_toast_with_activations(std::move(new_version_ready), 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{ strings.GITHUB_NEW_VERSION_UPDATE_NOW,
link_button{ GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART, L"powertoys://schedule_update/" + info.installer_filename }, L"powertoys://update_now/" + 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_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)); std::move(toast_params));
} }
void show_uninstallation_error() void show_uninstallation_error(const notifications::strings& strings)
{ {
remove_toasts(UPDATING_PROCESS_TOAST_TAG); 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; progress_bar_params progress_bar_params;
std::wstring progress_title{ info.version_string }; std::wstring progress_title{ info.version_string };
progress_title += L' '; 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_title = progress_title;
progress_bar_params.progress = progress; progress_bar_params.progress = progress;
update_toast_progress_bar(UPDATING_PROCESS_TOAST_TAG, progress_bar_params); update_toast_progress_bar(UPDATING_PROCESS_TOAST_TAG, progress_bar_params);

View File

@ -6,14 +6,66 @@ namespace updating
namespace notifications namespace notifications
{ {
void show_unavailable(); struct strings
void show_available(const updating::new_version_download_info& info); {
void show_download_start(const updating::new_version_download_info& info); std::wstring GITHUB_NEW_VERSION_AVAILABLE;
void show_visit_github(const updating::new_version_download_info& info); std::wstring GITHUB_NEW_VERSION_DOWNLOAD_STARTED;
void show_install_error(const updating::new_version_download_info& info); std::wstring GITHUB_NEW_VERSION_READY_TO_INSTALL;
void show_version_ready(const updating::new_version_download_info& info); std::wstring GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR;
void show_uninstallation_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<typename = void>
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);
} }
} }

View File

@ -19,7 +19,7 @@
#include "VersionHelper.h" #include "VersionHelper.h"
#include <PathCch.h> #include <PathCch.h>
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 POWER_TOYS_UPGRADE_CODE[] = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
const wchar_t POWERTOYS_EXE_COMPONENT[] = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}"; const wchar_t POWERTOYS_EXE_COMPONENT[] = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
@ -32,12 +32,6 @@ namespace
const wchar_t TOAST_TITLE[] = L"PowerToys"; 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 namespace updating
{ {
std::wstring get_msi_package_path() std::wstring get_msi_package_path()
@ -73,13 +67,18 @@ namespace updating
return package_path; 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; 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"); const auto uninstall_result = MsiInstallProductW(package_path.c_str(), L"REMOVE=ALL");
if (ERROR_SUCCESS == uninstall_result) if (ERROR_SUCCESS == uninstall_result)
@ -94,7 +93,7 @@ namespace updating
} }
catch (...) catch (...)
{ {
updating::notifications::show_uninstallation_error(); updating::notifications::show_uninstallation_error(strings);
} }
} }
return false; return false;
@ -202,7 +201,7 @@ namespace updating
return installer_download_dst; return installer_download_dst;
} }
std::future<void> try_autoupdate(const bool download_updates_automatically) std::future<void> try_autoupdate(const bool download_updates_automatically, const notifications::strings& strings)
{ {
const auto new_version = co_await get_new_github_version_info_async(); const auto new_version = co_await get_new_github_version_info_async();
if (!new_version) if (!new_version)
@ -230,32 +229,32 @@ namespace updating
} }
if (!download_success) if (!download_success)
{ {
updating::notifications::show_install_error(new_version.value()); updating::notifications::show_install_error(new_version.value(), strings);
co_return; co_return;
} }
updating::notifications::show_version_ready(new_version.value()); updating::notifications::show_version_ready(new_version.value(), strings);
} }
else else
{ {
updating::notifications::show_visit_github(new_version.value()); updating::notifications::show_visit_github(new_version.value(), strings);
} }
} }
std::future<std::wstring> check_new_version_available() std::future<std::wstring> check_new_version_available(const notifications::strings& strings)
{ {
const auto new_version = co_await get_new_github_version_info_async(); const auto new_version = co_await get_new_github_version_info_async();
if (!new_version) if (!new_version)
{ {
updating::notifications::show_unavailable(); updating::notifications::show_unavailable(strings);
co_return VersionHelper{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION }.toWstring(); 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; co_return new_version->version_string;
} }
std::future<std::wstring> download_update() std::future<std::wstring> download_update(const notifications::strings& strings)
{ {
const auto new_version = co_await get_new_github_version_info_async(); const auto new_version = co_await get_new_github_version_info_async();
if (!new_version) if (!new_version)
@ -264,12 +263,12 @@ namespace updating
} }
auto installer_download_dst = create_download_path() / new_version->installer_filename; 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 try
{ {
auto progressUpdateHandle = [&](float progress) { 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; http::HttpClient client;
@ -277,7 +276,7 @@ namespace updating
} }
catch (...) catch (...)
{ {
updating::notifications::show_install_error(new_version.value()); updating::notifications::show_install_error(new_version.value(), strings);
co_return L""; co_return L"";
} }

View File

@ -6,13 +6,14 @@
#include <filesystem> #include <filesystem>
#include <winrt/Windows.Foundation.h> #include <winrt/Windows.Foundation.h>
#include "notifications.h"
#include "../VersionHelper.h" #include "../VersionHelper.h"
namespace updating namespace updating
{ {
std::wstring get_msi_package_path(); std::wstring get_msi_package_path();
bool uninstall_msi_version(const std::wstring& package_path); bool uninstall_msi_version(const std::wstring& package_path, const notifications::strings&);
bool offer_msi_uninstallation(); bool offer_msi_uninstallation(const notifications::strings&);
std::optional<std::wstring> get_msi_package_installed_path(); std::optional<std::wstring> get_msi_package_installed_path();
std::optional<VersionHelper> get_installed_powertoys_version(); std::optional<VersionHelper> get_installed_powertoys_version();
@ -27,11 +28,11 @@ namespace updating
}; };
std::future<std::optional<new_version_download_info>> get_new_github_version_info_async(); std::future<std::optional<new_version_download_info>> get_new_github_version_info_async();
std::future<void> try_autoupdate(const bool download_updates_automatically); std::future<void> try_autoupdate(const bool download_updates_automatically, const notifications::strings&);
std::filesystem::path get_pending_updates_path(); std::filesystem::path get_pending_updates_path();
std::future<std::wstring> check_new_version_available(); std::future<std::wstring> check_new_version_available(const notifications::strings&);
std::future<std::wstring> download_update(); std::future<std::wstring> download_update(const notifications::strings&);
// non-localized // non-localized
constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN = L"powertoyssetup"; constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN = L"powertoyssetup";

View File

@ -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);
}
}

View File

@ -41,6 +41,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="System.IO.Abstractions" Version="12.2.5" />
<PackageReference Include="System.Text.Json" Version="4.7.2" /> <PackageReference Include="System.Text.Json" Version="4.7.2" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers"> <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers">
<Version>3.3.0</Version> <Version>3.3.0</Version>

View File

@ -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);
}
/// <summary>
/// Get path to the json settings file.
/// </summary>
/// <returns>string path.</returns>
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}");
}
}
}

View File

@ -5,6 +5,7 @@
using System; using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.IO.Abstractions;
using System.Text.Json; using System.Text.Json;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces; using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Microsoft.PowerToys.Settings.UI.Library.Utilities; 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 DefaultFileName = "settings.json";
private const string DefaultModuleName = ""; 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}")); _file = file ?? throw new ArgumentNullException(nameof(file));
} _settingsPath = settingPath;
public void DeleteSettings(string powertoy = "")
{
_ioProvider.DeleteDirectory(System.IO.Path.Combine(LocalApplicationDataFolder(), $"Microsoft\\PowerToys\\{powertoy}"));
}
/// <summary>
/// Get path to the json settings file.
/// </summary>
/// <returns>string path.</returns>
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}");
} }
public bool SettingsExists(string powertoy = DefaultModuleName, string fileName = DefaultFileName) 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);
} }
/// <summary> /// <summary>
@ -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 // 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. // 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 // 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<T>(jsonSettingsString); return JsonSerializer.Deserialize<T>(jsonSettingsString);
} }
@ -118,12 +104,12 @@ namespace Microsoft.PowerToys.Settings.UI.Library
{ {
if (jsonSettings != null) 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) catch (Exception e)
@ -137,10 +123,5 @@ namespace Microsoft.PowerToys.Settings.UI.Library
#endif #endif
} }
} }
private static string LocalApplicationDataFolder()
{
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
}
} }
} }

View File

@ -5,6 +5,7 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Abstractions;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Microsoft.PowerToys.Settings.UI.Library.CustomAction; using Microsoft.PowerToys.Settings.UI.Library.CustomAction;
@ -13,6 +14,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Utilities
{ {
public static class Helper public static class Helper
{ {
public static readonly IFileSystem FileSystem = new FileSystem();
public static bool AllowRunnerToForeground() public static bool AllowRunnerToForeground()
{ {
var result = false; var result = false;
@ -47,22 +50,20 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Utilities
return sendCustomAction.ToJsonString(); 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 var watcher = FileSystem.FileSystemWatcher.CreateNew();
{ watcher.Path = path;
Path = path, watcher.Filter = fileName;
Filter = fileName, watcher.NotifyFilter = NotifyFilters.LastWrite;
NotifyFilter = NotifyFilters.LastWrite, watcher.EnableRaisingEvents = true;
EnableRaisingEvents = true,
};
watcher.Changed += (o, e) => onChangedCallback(); watcher.Changed += (o, e) => onChangedCallback();

View File

@ -5,12 +5,16 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO.Abstractions;
namespace Microsoft.PowerToys.Settings.UI.Library.Utilities namespace Microsoft.PowerToys.Settings.UI.Library.Utilities
{ {
public static class Logger 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"); private static readonly string ApplicationLogPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\PowerToys\\Settings Logs");
static Logger() static Logger()
@ -20,6 +24,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Utilities
Directory.CreateDirectory(ApplicationLogPath); 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"); var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));

View File

@ -2,41 +2,61 @@
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // 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 namespace Microsoft.PowerToys.Settings.UI.Library.Utilities
{ {
public class SystemIOProvider : IIOProvider 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) public bool CreateDirectory(string path)
{ {
var directoryInfo = Directory.CreateDirectory(path); var directoryInfo = _directory.CreateDirectory(path);
return directoryInfo != null; return directoryInfo != null;
} }
public void DeleteDirectory(string path) public void DeleteDirectory(string path)
{ {
Directory.Delete(path, recursive: true); _directory.Delete(path, recursive: true);
} }
public bool DirectoryExists(string path) public bool DirectoryExists(string path)
{ {
return Directory.Exists(path); return _directory.Exists(path);
} }
public bool FileExists(string path) public bool FileExists(string path)
{ {
return File.Exists(path); return _file.Exists(path);
} }
public string ReadAllText(string path) public string ReadAllText(string path)
{ {
return File.ReadAllText(path); return _file.ReadAllText(path);
} }
public void WriteAllText(string path, string content) public void WriteAllText(string path, string content)
{ {
File.WriteAllText(path, content); _file.WriteAllText(path, content);
} }
} }
} }

View File

@ -1,13 +1,11 @@
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces; using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks;
using Moq; using Moq;
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO.Abstractions;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Text;
namespace Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility 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}"; 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<Func<string, bool>> SettingsFilterExpression = s => s == null || s.Contains("Microsoft\\PowerToys\\settings.json", StringComparison.Ordinal);
internal class MockSettingsRepository<T> : ISettingsRepository<T> where T : ISettingsConfig, new() internal class MockSettingsRepository<T> : ISettingsRepository<T> where T : ISettingsConfig, new()
{ {
T _settingsConfig; T _settingsConfig;
@ -43,44 +44,56 @@ namespace Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility
} }
public static Mock<IIOProvider>GetModuleIOProvider(string version, string module, string fileName) public static Mock<IFile>GetModuleIOProvider(string version, string module, string fileName)
{ {
var stubSettingsPath = string.Format(CultureInfo.InvariantCulture, BackCompatTestProperties.RootPathStubFiles, version, module, fileName); var stubSettingsPath = StubSettingsPath(version, module, fileName);
Expression<Func<string, bool>> filterExpression = (string s) => s.Contains(module, StringComparison.Ordinal); Expression<Func<string, bool>> filterExpression = ModuleFilterExpression(module);
var mockIOProvider = IIOProviderMocks.GetMockIOReadWithStubFile(stubSettingsPath, filterExpression); return IIOProviderMocks.GetMockIOReadWithStubFile(stubSettingsPath, filterExpression);
return mockIOProvider;
} }
public static void VerifyModuleIOProviderWasRead(Mock<IIOProvider> 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<IFile> provider, string module, int expectedCallCount)
{ {
if(provider == null) if(provider == null)
{ {
throw new ArgumentNullException(nameof(provider)); throw new ArgumentNullException(nameof(provider));
} }
Expression<Func<string, bool>> filterExpression = (string s) => s.Contains(module, StringComparison.Ordinal); Expression<Func<string, bool>> filterExpression = ModuleFilterExpression(module);
IIOProviderMocks.VerifyIOReadWithStubFile(provider, filterExpression, expectedCallCount); IIOProviderMocks.VerifyIOReadWithStubFile(provider, filterExpression, expectedCallCount);
} }
public static Mock<IIOProvider> GetGeneralSettingsIOProvider(string version) private static Expression<Func<string, bool>> ModuleFilterExpression(string module)
{ {
var stubGeneralSettingsPath = string.Format(CultureInfo.InvariantCulture, BackCompatTestProperties.RootPathStubFiles, version, string.Empty, "settings.json"); // Using Ordinal since this is used internally for a path
Expression<Func<string, bool>> filterExpression = (string s) => s.Contains("Microsoft\\PowerToys\\settings.json", StringComparison.Ordinal); return s => s == null || s.Contains(module, StringComparison.Ordinal);
var mockGeneralIOProvider = IIOProviderMocks.GetMockIOReadWithStubFile(stubGeneralSettingsPath, filterExpression);
return mockGeneralIOProvider;
} }
public static void VerifyGeneralSettingsIOProviderWasRead(Mock<IIOProvider> provider, int expectedCallCount) public static Mock<IFile> GetGeneralSettingsIOProvider(string version)
{
var stubGeneralSettingsPath = StubGeneralSettingsPath(version);
return IIOProviderMocks.GetMockIOReadWithStubFile(stubGeneralSettingsPath, SettingsFilterExpression);
}
public static void VerifyGeneralSettingsIOProviderWasRead(Mock<IFile> provider, int expectedCallCount)
{ {
if (provider == null) if (provider == null)
{ {
throw new ArgumentNullException(nameof(provider)); throw new ArgumentNullException(nameof(provider));
} }
Expression<Func<string, bool>> filterExpression = (string s) => s.Contains("Microsoft\\PowerToys\\settings.json", StringComparison.Ordinal); IIOProviderMocks.VerifyIOReadWithStubFile(provider, SettingsFilterExpression, expectedCallCount);
IIOProviderMocks.VerifyIOReadWithStubFile(provider, filterExpression, expectedCallCount);
} }
} }

View File

@ -29,6 +29,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="12.2.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,10 +1,9 @@
using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Moq; using Moq;
using System; using System;
using System.Collections.Generic; using System.IO.Abstractions;
using System.IO; using System.IO.Abstractions.TestingHelpers;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Text;
namespace Microsoft.PowerToys.Settings.UI.UnitTests.Mocks namespace Microsoft.PowerToys.Settings.UI.UnitTests.Mocks
{ {
@ -26,11 +25,13 @@ namespace Microsoft.PowerToys.Settings.UI.UnitTests.Mocks
savePath = path; savePath = path;
saveContent = content; saveContent = content;
}); });
// Using Ordinal since this is used internally for a path
mockIOProvider.Setup(x => x.ReadAllText(It.Is<string>(x => x.Equals(savePath, StringComparison.Ordinal)))) mockIOProvider.Setup(x => x.ReadAllText(It.Is<string>(x => x.Equals(savePath, StringComparison.Ordinal))))
.Returns(() => saveContent); .Returns(() => saveContent);
// Using Ordinal since this is used internally for a path
mockIOProvider.Setup(x => x.FileExists(It.Is<string>(x => x.Equals(savePath, StringComparison.Ordinal)))) mockIOProvider.Setup(x => x.FileExists(It.Is<string>(x => x.Equals(savePath, StringComparison.Ordinal))))
.Returns(true); .Returns(true);
// Using Ordinal since this is used internally for a path
mockIOProvider.Setup(x => x.FileExists(It.Is<string>(x => !x.Equals(savePath, StringComparison.Ordinal)))) mockIOProvider.Setup(x => x.FileExists(It.Is<string>(x => !x.Equals(savePath, StringComparison.Ordinal))))
.Returns(false); .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;
/// <summary> /// <summary>
/// This method mocks an IO provider so that it will always return data at the savePath location. /// 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. /// 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
/// <param name="savePath">The path to the stub settings file</param> /// <param name="savePath">The path to the stub settings file</param>
/// <param name="expectedPathSubstring">The substring in the path that identifies the module eg. Microsoft\\PowerToys\\ColorPicker</param> /// <param name="expectedPathSubstring">The substring in the path that identifies the module eg. Microsoft\\PowerToys\\ColorPicker</param>
/// <returns></returns> /// <returns></returns>
internal static Mock<IIOProvider> GetMockIOReadWithStubFile(string savePath, Expression<Func<string, bool>> filterExpression) internal static Mock<IFile> GetMockIOReadWithStubFile(string savePath, Expression<Func<string, bool>> filterExpression)
{ {
string saveContent = File.ReadAllText(savePath); string saveContent = File.ReadAllText(savePath);
var mockIOProvider = new Mock<IIOProvider>(); var fileMock = new Mock<IFile>();
mockIOProvider.Setup(x => x.ReadAllText(It.Is<string>(filterExpression))) fileMock.Setup(x => x.ReadAllText(It.Is<string>(filterExpression)))
.Returns(() => saveContent).Verifiable(); .Returns(() => saveContent).Verifiable();
mockIOProvider.Setup(x => x.FileExists(It.Is<string>(filterExpression))) fileMock.Setup(x => x.Exists(It.Is<string>(filterExpression)))
.Returns(true); .Returns(true);
return mockIOProvider; return fileMock;
} }
internal static void VerifyIOReadWithStubFile(Mock<IIOProvider> mockIOProvider, Expression<Func<string, bool>> filterExpression, int expectedCallCount) internal static void VerifyIOReadWithStubFile(Mock<IFile> fileMock, Expression<Func<string, bool>> filterExpression, int expectedCallCount)
{ {
mockIOProvider.Verify(x => x.ReadAllText(It.Is<string>(filterExpression)), Times.Exactly(expectedCallCount)); fileMock.Verify(x => x.ReadAllText(It.Is<string>(filterExpression)), Times.Exactly(expectedCallCount));
} }
} }
} }

View File

@ -6,9 +6,9 @@ using System;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks;
using System.IO.Abstractions.TestingHelpers;
using Microsoft.PowerToys.Settings.UnitTest; using Microsoft.PowerToys.Settings.UnitTest;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema; using Newtonsoft.Json.Schema;
@ -25,11 +25,8 @@ namespace CommonLibTest
public void ToJsonStringShouldReturnValidJSONOfModelWhenSuccessful() public void ToJsonStringShouldReturnValidJSONOfModelWhenSuccessful()
{ {
//Mock Disk access //Mock Disk access
string saveContent = string.Empty; var mockFileSystem = new MockFileSystem();
string savePath = string.Empty; var settingsUtils = new SettingsUtils(mockFileSystem);
var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists();
var settingsUtils = new SettingsUtils(mockIOProvider.Object);
// Arrange // Arrange
string file_name = "test\\BasePTModuleSettingsTest"; string file_name = "test\\BasePTModuleSettingsTest";

View File

@ -2,9 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;

View File

@ -3,11 +3,14 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.IO; using System.IO.Abstractions;
using System.IO.Abstractions.TestingHelpers;
using System.Linq; using System.Linq;
using System.Text.Json; using System.Text.Json;
using Microsoft.PowerToys.Settings.UI.Library; 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.Library.Utilities;
using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility;
using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks;
using Microsoft.PowerToys.Settings.UnitTest; using Microsoft.PowerToys.Settings.UnitTest;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
@ -19,13 +22,12 @@ namespace CommonLibTest
public class SettingsUtilsTests public class SettingsUtilsTests
{ {
[TestMethod] [TestMethod]
public void SaveSettingsSaveSettingsToFileWhenFilePathExists() public void SaveSettingsSaveSettingsToFileWhenFilePathExists()
{ {
// Arrange // Arrange
var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); var mockFileSystem = new MockFileSystem();
var settingsUtils = new SettingsUtils(mockIOProvider.Object); var settingsUtils = new SettingsUtils(mockFileSystem);
string file_name = "\\test"; string file_name = "\\test";
string file_contents_correct_json_content = "{\"name\":\"powertoy module name\",\"version\":\"powertoy version\"}"; string file_contents_correct_json_content = "{\"name\":\"powertoy module name\",\"version\":\"powertoy version\"}";
@ -44,8 +46,8 @@ namespace CommonLibTest
public void SaveSettingsShouldCreateFileWhenFilePathIsNotFound() public void SaveSettingsShouldCreateFileWhenFilePathIsNotFound()
{ {
// Arrange // Arrange
var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); var mockFileSystem = new MockFileSystem();
var settingsUtils = new SettingsUtils(mockIOProvider.Object); var settingsUtils = new SettingsUtils(mockFileSystem);
string file_name = "test\\Test Folder"; string file_name = "test\\Test Folder";
string file_contents_correct_json_content = "{\"name\":\"powertoy module name\",\"version\":\"powertoy version\"}"; string file_contents_correct_json_content = "{\"name\":\"powertoy module name\",\"version\":\"powertoy version\"}";
@ -62,8 +64,8 @@ namespace CommonLibTest
public void SettingsFolderExistsShouldReturnFalseWhenFilePathIsNotFound() public void SettingsFolderExistsShouldReturnFalseWhenFilePathIsNotFound()
{ {
// Arrange // Arrange
var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); var mockFileSystem = new MockFileSystem();
var settingsUtils = new SettingsUtils(mockIOProvider.Object); var settingsUtils = new SettingsUtils(mockFileSystem);
string file_name_random = "test\\" + RandomString(); string file_name_random = "test\\" + RandomString();
string file_name_exists = "test\\exists"; string file_name_exists = "test\\exists";
string file_contents_correct_json_content = "{\"name\":\"powertoy module name\",\"version\":\"powertoy version\"}"; string file_contents_correct_json_content = "{\"name\":\"powertoy module name\",\"version\":\"powertoy version\"}";
@ -79,6 +81,21 @@ namespace CommonLibTest
Assert.IsTrue(pathFound); Assert.IsTrue(pathFound);
} }
[TestMethod]
public void SettingsUtilsMustReturnDefaultItemWhenFileIsCorrupt()
{
// Arrange
var mockFileSystem = new MockFileSystem();
var mockSettingsUtils = new SettingsUtils(mockFileSystem);
// Act
TestClass settings = mockSettingsUtils.GetSettings<TestClass>(string.Empty);
// Assert
Assert.AreEqual(settings.TestInt, 100);
Assert.AreEqual(settings.TestString, "test");
}
public static string RandomString() public static string RandomString()
{ {
Random random = new Random(); Random random = new Random();
@ -88,5 +105,26 @@ namespace CommonLibTest
return new string(Enumerable.Repeat(chars, length) return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray()); .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();
}
}
} }
} }

View File

@ -2,14 +2,13 @@
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System.Globalization;
using System.IO;
using System.Text.Json; using System.Text.Json;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.ViewModels; using Microsoft.PowerToys.Settings.UI.Library.ViewModels;
using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility; using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility;
using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace ViewModelTests namespace ViewModelTests
{ {
@ -27,11 +26,14 @@ namespace ViewModelTests
{ {
//Arrange //Arrange
var mockIOProvider = BackCompatTestProperties.GetModuleIOProvider(version, ColorPickerSettings.ModuleName, fileName); var mockIOProvider = BackCompatTestProperties.GetModuleIOProvider(version, ColorPickerSettings.ModuleName, fileName);
var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); var settingPathMock = new Mock<ISettingsPath>();
var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object, settingPathMock.Object);
ColorPickerSettings originalSettings = mockSettingsUtils.GetSettings<ColorPickerSettings>(ColorPickerSettings.ModuleName); ColorPickerSettings originalSettings = mockSettingsUtils.GetSettings<ColorPickerSettings>(ColorPickerSettings.ModuleName);
var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version);
var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object);
var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object, settingPathMock.Object);
GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>(); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>();
var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils); var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils);

View File

@ -3,9 +3,6 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Text.Json; using System.Text.Json;
using CommonLibTest; using CommonLibTest;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library;
@ -33,13 +30,16 @@ namespace ViewModelTests
[DataRow("v0.22.0", "settings.json")] [DataRow("v0.22.0", "settings.json")]
public void OriginalFilesModificationTest(string version, string fileName) public void OriginalFilesModificationTest(string version, string fileName)
{ {
var mockIOProvider = BackCompatTestProperties.GetModuleIOProvider(version, FancyZonesSettings.ModuleName, fileName); var settingPathMock = new Mock<ISettingsPath>();
var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object);
var fileMock = BackCompatTestProperties.GetModuleIOProvider(version, FancyZonesSettings.ModuleName, fileName);
var mockSettingsUtils = new SettingsUtils(fileMock.Object, settingPathMock.Object);
FancyZonesSettings originalSettings = mockSettingsUtils.GetSettings<FancyZonesSettings>(FancyZonesSettings.ModuleName); FancyZonesSettings originalSettings = mockSettingsUtils.GetSettings<FancyZonesSettings>(FancyZonesSettings.ModuleName);
var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version);
var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object); var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object, settingPathMock.Object);
GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>(); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>();
var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils); var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils);
var fancyZonesRepository = new BackCompatTestProperties.MockSettingsRepository<FancyZonesSettings>(mockSettingsUtils); var fancyZonesRepository = new BackCompatTestProperties.MockSettingsRepository<FancyZonesSettings>(mockSettingsUtils);
@ -71,7 +71,7 @@ namespace ViewModelTests
//Verify that the stub file was used //Verify that the stub file was used
var expectedCallCount = 2; //once via the view model, and once by the test (GetSettings<T>) var expectedCallCount = 2; //once via the view model, and once by the test (GetSettings<T>)
BackCompatTestProperties.VerifyModuleIOProviderWasRead(mockIOProvider, FancyZonesSettings.ModuleName, expectedCallCount); BackCompatTestProperties.VerifyModuleIOProviderWasRead(fileMock, FancyZonesSettings.ModuleName, expectedCallCount);
BackCompatTestProperties.VerifyGeneralSettingsIOProviderWasRead(mockGeneralIOProvider, expectedCallCount); BackCompatTestProperties.VerifyGeneralSettingsIOProviderWasRead(mockGeneralIOProvider, expectedCallCount);
} }

View File

@ -4,7 +4,6 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Text.Json; using System.Text.Json;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.ViewModels; 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.PowerToys.Settings.UI.UnitTests.Mocks;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq; using Moq;
using NuGet.Frameworks; using JsonSerializer = System.Text.Json.JsonSerializer;
namespace ViewModelTests namespace ViewModelTests
{ {
@ -40,16 +39,20 @@ namespace ViewModelTests
[DataRow("v0.22.0")] [DataRow("v0.22.0")]
public void OriginalFilesModificationTest(string version) public void OriginalFilesModificationTest(string version)
{ {
var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); var settingPathMock = new Mock<ISettingsPath>();
var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object); var fileMock = BackCompatTestProperties.GetGeneralSettingsIOProvider(version);
var mockGeneralSettingsUtils = new SettingsUtils(fileMock.Object, settingPathMock.Object);
GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>(); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>();
var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils); var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils);
// Initialise View Model with test Config files // Initialise View Model with test Config files
// Arrange // Arrange
Func<string, int> SendMockIPCConfigMSG = msg => { return 0; }; Func<string, int> SendMockIPCConfigMSG = msg => 0;
Func<string, int> SendRestartAdminIPCMessage = msg => { return 0; }; Func<string, int> SendRestartAdminIPCMessage = msg => 0;
Func<string, int> SendCheckForUpdatesIPCMessage = msg => { return 0; }; Func<string, int> SendCheckForUpdatesIPCMessage = msg => 0;
var viewModel = new GeneralViewModel( var viewModel = new GeneralViewModel(
settingsRepository: generalSettingsRepository, settingsRepository: generalSettingsRepository,
runAsAdminText: "GeneralSettings_RunningAsAdminText", runAsAdminText: "GeneralSettings_RunningAsAdminText",
@ -71,7 +74,7 @@ namespace ViewModelTests
//Verify that the stub file was used //Verify that the stub file was used
var expectedCallCount = 2; //once via the view model, and once by the test (GetSettings<T>) var expectedCallCount = 2; //once via the view model, and once by the test (GetSettings<T>)
BackCompatTestProperties.VerifyGeneralSettingsIOProviderWasRead(mockGeneralIOProvider, expectedCallCount); BackCompatTestProperties.VerifyGeneralSettingsIOProviderWasRead(fileMock, expectedCallCount);
} }
[TestMethod] [TestMethod]
@ -82,7 +85,7 @@ namespace ViewModelTests
Func<string, int> SendRestartAdminIPCMessage = msg => { return 0; }; Func<string, int> SendRestartAdminIPCMessage = msg => { return 0; };
Func<string, int> SendCheckForUpdatesIPCMessage = msg => { return 0; }; Func<string, int> SendCheckForUpdatesIPCMessage = msg => { return 0; };
GeneralViewModel viewModel = new GeneralViewModel( GeneralViewModel viewModel = new GeneralViewModel(
SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
"GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsAdminText",
"GeneralSettings_RunningAsUserText", "GeneralSettings_RunningAsUserText",
false, false,
@ -119,7 +122,7 @@ namespace ViewModelTests
Func<string, int> SendRestartAdminIPCMessage = msg => { return 0; }; Func<string, int> SendRestartAdminIPCMessage = msg => { return 0; };
Func<string, int> SendCheckForUpdatesIPCMessage = msg => { return 0; }; Func<string, int> SendCheckForUpdatesIPCMessage = msg => { return 0; };
GeneralViewModel viewModel = new GeneralViewModel( GeneralViewModel viewModel = new GeneralViewModel(
SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
"GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsAdminText",
"GeneralSettings_RunningAsUserText", "GeneralSettings_RunningAsUserText",
false, false,
@ -151,7 +154,7 @@ namespace ViewModelTests
// Arrange // Arrange
GeneralViewModel viewModel = new GeneralViewModel( GeneralViewModel viewModel = new GeneralViewModel(
SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
"GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsAdminText",
"GeneralSettings_RunningAsUserText", "GeneralSettings_RunningAsUserText",
false, false,
@ -184,7 +187,7 @@ namespace ViewModelTests
Func<string, int> SendRestartAdminIPCMessage = msg => { return 0; }; Func<string, int> SendRestartAdminIPCMessage = msg => { return 0; };
Func<string, int> SendCheckForUpdatesIPCMessage = msg => { return 0; }; Func<string, int> SendCheckForUpdatesIPCMessage = msg => { return 0; };
viewModel = new GeneralViewModel( viewModel = new GeneralViewModel(
SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
"GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsAdminText",
"GeneralSettings_RunningAsUserText", "GeneralSettings_RunningAsUserText",
false, false,
@ -215,7 +218,7 @@ namespace ViewModelTests
Func<string, int> SendRestartAdminIPCMessage = msg => { return 0; }; Func<string, int> SendRestartAdminIPCMessage = msg => { return 0; };
Func<string, int> SendCheckForUpdatesIPCMessage = msg => { return 0; }; Func<string, int> SendCheckForUpdatesIPCMessage = msg => { return 0; };
GeneralViewModel viewModel = new GeneralViewModel( GeneralViewModel viewModel = new GeneralViewModel(
SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
"GeneralSettings_RunningAsAdminText", "GeneralSettings_RunningAsAdminText",
"GeneralSettings_RunningAsUserText", "GeneralSettings_RunningAsUserText",
false, false,

View File

@ -3,8 +3,8 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.Globalization; using System.IO.Abstractions;
using System.IO; using System.IO.Abstractions.TestingHelpers;
using System.Linq; using System.Linq;
using System.Text.Json; using System.Text.Json;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library;
@ -20,16 +20,15 @@ namespace ViewModelTests
[TestClass] [TestClass]
public class ImageResizer public class ImageResizer
{ {
private Mock<ISettingsUtils> _mockGeneralSettingsUtils;
private Mock<ISettingsUtils> mockGeneralSettingsUtils; private Mock<ISettingsUtils> _mockImgResizerSettingsUtils;
private Mock<ISettingsUtils> mockImgResizerSettingsUtils;
[TestInitialize] [TestInitialize]
public void SetUpStubSettingUtils() public void SetUpStubSettingUtils()
{ {
mockGeneralSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils<GeneralSettings>(); _mockGeneralSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils<GeneralSettings>();
mockImgResizerSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils<ImageResizerSettings>(); _mockImgResizerSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils<ImageResizerSettings>();
} }
@ -44,13 +43,17 @@ namespace ViewModelTests
[DataRow("v0.22.0", "settings.json")] [DataRow("v0.22.0", "settings.json")]
public void OriginalFilesModificationTest(string version, string fileName) public void OriginalFilesModificationTest(string version, string fileName)
{ {
var mockIOProvider = BackCompatTestProperties.GetModuleIOProvider(version, ImageResizerSettings.ModuleName, fileName); var settingPathMock = new Mock<ISettingsPath>();
var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object);
var fileMock = BackCompatTestProperties.GetModuleIOProvider(version, ImageResizerSettings.ModuleName, fileName);
var mockSettingsUtils = new SettingsUtils(fileMock.Object, settingPathMock.Object);
ImageResizerSettings originalSettings = mockSettingsUtils.GetSettings<ImageResizerSettings>(ImageResizerSettings.ModuleName); ImageResizerSettings originalSettings = mockSettingsUtils.GetSettings<ImageResizerSettings>(ImageResizerSettings.ModuleName);
var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); var mockGeneralFileMock = BackCompatTestProperties.GetGeneralSettingsIOProvider(version);
var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object); var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralFileMock.Object, settingPathMock.Object);
GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>(); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>();
var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils); var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils);
// Initialise View Model with test Config files // Initialise View Model with test Config files
@ -69,8 +72,8 @@ namespace ViewModelTests
//Verify that the stub file was used //Verify that the stub file was used
var expectedCallCount = 2; //once via the view model, and once by the test (GetSettings<T>) var expectedCallCount = 2; //once via the view model, and once by the test (GetSettings<T>)
BackCompatTestProperties.VerifyModuleIOProviderWasRead(mockIOProvider, ImageResizerSettings.ModuleName, expectedCallCount); BackCompatTestProperties.VerifyModuleIOProviderWasRead(fileMock, ImageResizerSettings.ModuleName, expectedCallCount);
BackCompatTestProperties.VerifyGeneralSettingsIOProviderWasRead(mockGeneralIOProvider, expectedCallCount); BackCompatTestProperties.VerifyGeneralSettingsIOProviderWasRead(mockGeneralFileMock, expectedCallCount);
} }
[TestMethod] [TestMethod]
@ -85,7 +88,7 @@ namespace ViewModelTests
}; };
// arrange // arrange
ImageResizerViewModel viewModel = new ImageResizerViewModel(mockImgResizerSettingsUtils.Object, SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); ImageResizerViewModel viewModel = new ImageResizerViewModel(_mockImgResizerSettingsUtils.Object, SettingsRepository<GeneralSettings>.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG);
// act // act
viewModel.IsEnabled = true; viewModel.IsEnabled = true;
@ -95,16 +98,16 @@ namespace ViewModelTests
public void JPEGQualityLevelShouldSetValueToTenWhenSuccessful() public void JPEGQualityLevelShouldSetValueToTenWhenSuccessful()
{ {
// arrange // arrange
var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); var fileSystemMock = new MockFileSystem();
var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); var mockSettingsUtils = new SettingsUtils(fileSystemMock);
Func<string, int> SendMockIPCConfigMSG = msg => { return 0; }; Func<string, int> SendMockIPCConfigMSG = msg => { return 0; };
ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG);
// act // act
viewModel.JPEGQualityLevel = 10; viewModel.JPEGQualityLevel = 10;
// Assert // Assert
viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG);
Assert.AreEqual(10, viewModel.JPEGQualityLevel); Assert.AreEqual(10, viewModel.JPEGQualityLevel);
} }
@ -112,16 +115,16 @@ namespace ViewModelTests
public void PngInterlaceOptionShouldSetValueToTenWhenSuccessful() public void PngInterlaceOptionShouldSetValueToTenWhenSuccessful()
{ {
// arrange // arrange
var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); var fileSystemMock = new MockFileSystem();
var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); var mockSettingsUtils = new SettingsUtils(fileSystemMock);
Func<string, int> SendMockIPCConfigMSG = msg => { return 0; }; Func<string, int> SendMockIPCConfigMSG = msg => { return 0; };
ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG);
// act // act
viewModel.PngInterlaceOption = 10; viewModel.PngInterlaceOption = 10;
// Assert // Assert
viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG);
Assert.AreEqual(10, viewModel.PngInterlaceOption); Assert.AreEqual(10, viewModel.PngInterlaceOption);
} }
@ -129,16 +132,16 @@ namespace ViewModelTests
public void TiffCompressOptionShouldSetValueToTenWhenSuccessful() public void TiffCompressOptionShouldSetValueToTenWhenSuccessful()
{ {
// arrange // arrange
var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); var fileSystemMock = new MockFileSystem();
var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); var mockSettingsUtils = new SettingsUtils(fileSystemMock);
Func<string, int> SendMockIPCConfigMSG = msg => { return 0; }; Func<string, int> SendMockIPCConfigMSG = msg => { return 0; };
ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG);
// act // act
viewModel.TiffCompressOption = 10; viewModel.TiffCompressOption = 10;
// Assert // Assert
viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG);
Assert.AreEqual(10, viewModel.TiffCompressOption); Assert.AreEqual(10, viewModel.TiffCompressOption);
} }
@ -146,17 +149,17 @@ namespace ViewModelTests
public void FileNameShouldUpdateValueWhenSuccessful() public void FileNameShouldUpdateValueWhenSuccessful()
{ {
// arrange // arrange
var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); var fileSystemMock = new MockFileSystem();
var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); var mockSettingsUtils = new SettingsUtils(fileSystemMock);
Func<string, int> SendMockIPCConfigMSG = msg => { return 0; }; Func<string, int> SendMockIPCConfigMSG = msg => { return 0; };
ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG);
string expectedValue = "%1 (%3)"; string expectedValue = "%1 (%3)";
// act // act
viewModel.FileName = expectedValue; viewModel.FileName = expectedValue;
// Assert // Assert
viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG);
Assert.AreEqual(expectedValue, viewModel.FileName); Assert.AreEqual(expectedValue, viewModel.FileName);
} }
@ -167,6 +170,7 @@ namespace ViewModelTests
var settingUtils = ISettingsUtilsMocks.GetStubSettingsUtils<ImageResizerSettings>(); var settingUtils = ISettingsUtilsMocks.GetStubSettingsUtils<ImageResizerSettings>();
var expectedSettingsString = new ImageResizerSettings() { Properties = new ImageResizerProperties() { ImageresizerKeepDateModified = new BoolProperty() { Value = true } } }.ToJsonString(); 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( settingUtils.Setup(x => x.SaveSettings(
It.Is<string>(content => content.Equals(expectedSettingsString, StringComparison.Ordinal)), It.Is<string>(content => content.Equals(expectedSettingsString, StringComparison.Ordinal)),
It.Is<string>(module => module.Equals(ImageResizerSettings.ModuleName, StringComparison.Ordinal)), It.Is<string>(module => module.Equals(ImageResizerSettings.ModuleName, StringComparison.Ordinal)),
@ -174,7 +178,7 @@ namespace ViewModelTests
.Verifiable(); .Verifiable();
Func<string, int> SendMockIPCConfigMSG = msg => { return 0; }; Func<string, int> SendMockIPCConfigMSG = msg => { return 0; };
ImageResizerViewModel viewModel = new ImageResizerViewModel(settingUtils.Object, SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); ImageResizerViewModel viewModel = new ImageResizerViewModel(settingUtils.Object, SettingsRepository<GeneralSettings>.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG);
// act // act
viewModel.KeepDateModified = true; viewModel.KeepDateModified = true;
@ -187,16 +191,16 @@ namespace ViewModelTests
public void EncoderShouldUpdateValueWhenSuccessful() public void EncoderShouldUpdateValueWhenSuccessful()
{ {
// arrange // arrange
var mockIOProvider = IIOProviderMocks.GetMockIOProviderForSaveLoadExists(); var fileSystemMock = new MockFileSystem();
var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); var mockSettingsUtils = new SettingsUtils(fileSystemMock);
Func<string, int> SendMockIPCConfigMSG = msg => { return 0; }; Func<string, int> SendMockIPCConfigMSG = msg => { return 0; };
ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG);
// act // act
viewModel.Encoder = 3; viewModel.Encoder = 3;
// Assert // Assert
viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); viewModel = new ImageResizerViewModel(mockSettingsUtils, SettingsRepository<GeneralSettings>.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG);
Assert.AreEqual("163bcc30-e2e9-4f0b-961d-a3e9fdb788a3", viewModel.EncoderGuid); Assert.AreEqual("163bcc30-e2e9-4f0b-961d-a3e9fdb788a3", viewModel.EncoderGuid);
Assert.AreEqual(3, viewModel.Encoder); Assert.AreEqual(3, viewModel.Encoder);
} }
@ -207,7 +211,7 @@ namespace ViewModelTests
// arrange // arrange
var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils<ImageResizerSettings>(); var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils<ImageResizerSettings>();
Func<string, int> SendMockIPCConfigMSG = msg => { return 0; }; Func<string, int> SendMockIPCConfigMSG = msg => { return 0; };
ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils.Object, SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils.Object, SettingsRepository<GeneralSettings>.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG);
int sizeOfOriginalArray = viewModel.Sizes.Count; int sizeOfOriginalArray = viewModel.Sizes.Count;
// act // act
@ -223,7 +227,7 @@ namespace ViewModelTests
// arrange // arrange
var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils<ImageResizerSettings>(); var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils<ImageResizerSettings>();
Func<string, int> SendMockIPCConfigMSG = msg => { return 0; }; Func<string, int> SendMockIPCConfigMSG = msg => { return 0; };
ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils.Object, SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG); ImageResizerViewModel viewModel = new ImageResizerViewModel(mockSettingsUtils.Object, SettingsRepository<GeneralSettings>.GetInstance(_mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG);
int sizeOfOriginalArray = viewModel.Sizes.Count; int sizeOfOriginalArray = viewModel.Sizes.Count;
ImageSize deleteCandidate = viewModel.Sizes.Where<ImageSize>(x => x.Id == 0).First(); ImageSize deleteCandidate = viewModel.Sizes.Where<ImageSize>(x => x.Id == 0).First();

View File

@ -10,6 +10,7 @@ using Moq;
using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks;
using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility; using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility;
using System.Globalization; using System.Globalization;
using System.IO.Abstractions;
namespace ViewModelTests namespace ViewModelTests
{ {
@ -52,13 +53,16 @@ namespace ViewModelTests
[DataRow("v0.22.0", "settings.json")] [DataRow("v0.22.0", "settings.json")]
public void OriginalFilesModificationTest(string version, string fileName) public void OriginalFilesModificationTest(string version, string fileName)
{ {
var settingPathMock = new Mock<ISettingsPath>();
var mockIOProvider = BackCompatTestProperties.GetModuleIOProvider(version, PowerLauncherSettings.ModuleName, fileName); 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>(PowerLauncherSettings.ModuleName); PowerLauncherSettings originalSettings = mockSettingsUtils.GetSettings<PowerLauncherSettings>(PowerLauncherSettings.ModuleName);
var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version);
var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object); var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object, settingPathMock.Object);
GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>(); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>();
var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils); var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils);
// Initialise View Model with test Config files // Initialise View Model with test Config files

View File

@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.IO; using System.IO.Abstractions;
using System.Text.Json; using System.Text.Json;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.ViewModels; using Microsoft.PowerToys.Settings.UI.Library.ViewModels;
@ -40,13 +40,15 @@ namespace ViewModelTests
[DataRow("v0.22.0", "settings.json")] [DataRow("v0.22.0", "settings.json")]
public void OriginalFilesModificationTest(string version, string fileName) public void OriginalFilesModificationTest(string version, string fileName)
{ {
var mockIOProvider = BackCompatTestProperties.GetModuleIOProvider(version, PowerPreviewSettings.ModuleName, fileName); var settingPathMock = new Mock<ISettingsPath>();
var mockSettingsUtils = new SettingsUtils(mockIOProvider.Object); var fileMock = BackCompatTestProperties.GetModuleIOProvider(version, PowerPreviewSettings.ModuleName, fileName);
var mockSettingsUtils = new SettingsUtils(fileMock.Object, settingPathMock.Object);
PowerPreviewSettings originalSettings = mockSettingsUtils.GetSettings<PowerPreviewSettings>(PowerPreviewSettings.ModuleName); PowerPreviewSettings originalSettings = mockSettingsUtils.GetSettings<PowerPreviewSettings>(PowerPreviewSettings.ModuleName);
var repository = new BackCompatTestProperties.MockSettingsRepository<PowerPreviewSettings>(mockSettingsUtils); var repository = new BackCompatTestProperties.MockSettingsRepository<PowerPreviewSettings>(mockSettingsUtils);
var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version);
var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object); var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object, settingPathMock.Object);
GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>(); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>();
var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils); var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils);
@ -62,7 +64,7 @@ namespace ViewModelTests
//Verify that the stub file was used //Verify that the stub file was used
var expectedCallCount = 2; //once via the view model, and once by the test (GetSettings<T>) var expectedCallCount = 2; //once via the view model, and once by the test (GetSettings<T>)
BackCompatTestProperties.VerifyModuleIOProviderWasRead(mockIOProvider, PowerPreviewSettings.ModuleName, expectedCallCount); BackCompatTestProperties.VerifyModuleIOProviderWasRead(fileMock, PowerPreviewSettings.ModuleName, expectedCallCount);
} }

View File

@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.IO; using System.IO.Abstractions;
using System.Text.Json; using System.Text.Json;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.ViewModels; using Microsoft.PowerToys.Settings.UI.Library.ViewModels;
@ -40,12 +40,16 @@ namespace ViewModelTests
[DataRow("v0.22.0", "power-rename-settings.json")] [DataRow("v0.22.0", "power-rename-settings.json")]
public void OriginalFilesModificationTest(string version, string fileName) public void OriginalFilesModificationTest(string version, string fileName)
{ {
var settingPathMock = new Mock<ISettingsPath>();
var mockIOProvider = BackCompatTestProperties.GetModuleIOProvider(version, PowerRenameSettings.ModuleName, fileName); 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<PowerRenameLocalProperties>(PowerRenameSettings.ModuleName); PowerRenameLocalProperties originalSettings = mockSettingsUtils.GetSettings<PowerRenameLocalProperties>(PowerRenameSettings.ModuleName);
var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version);
var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object);
var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object, settingPathMock.Object);
GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>(); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>();
var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils); var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils);

View File

@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.IO;
using System.Text.Json; using System.Text.Json;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.ViewModels; using Microsoft.PowerToys.Settings.UI.Library.ViewModels;
@ -30,12 +29,13 @@ namespace ViewModelTests
[DataRow("v0.22.0", "settings.json")] [DataRow("v0.22.0", "settings.json")]
public void OriginalFilesModificationTest(string version, string fileName) public void OriginalFilesModificationTest(string version, string fileName)
{ {
var settingPathMock = new Mock<ISettingsPath>();
var mockIOProvider = BackCompatTestProperties.GetModuleIOProvider(version, ShortcutGuideSettings.ModuleName, fileName); 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>(ShortcutGuideSettings.ModuleName); ShortcutGuideSettings originalSettings = mockSettingsUtils.GetSettings<ShortcutGuideSettings>(ShortcutGuideSettings.ModuleName);
var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version);
var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object); var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object, settingPathMock.Object);
GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>(); GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings<GeneralSettings>();
var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils); var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<GeneralSettings>(mockGeneralSettingsUtils);
var shortcutSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<ShortcutGuideSettings>(mockSettingsUtils); var shortcutSettingsRepository = new BackCompatTestProperties.MockSettingsRepository<ShortcutGuideSettings>(mockSettingsUtils);

View File

@ -5,7 +5,6 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media;
@ -14,7 +13,7 @@ namespace Microsoft.PowerToys.Settings.UI.Converters
{ {
public sealed class ModuleEnabledToForegroundConverter : IValueConverter public sealed class ModuleEnabledToForegroundConverter : IValueConverter
{ {
private readonly ISettingsUtils settingsUtils = new SettingsUtils(new SystemIOProvider()); private readonly ISettingsUtils settingsUtils = new SettingsUtils();
private string selectedTheme = string.Empty; private string selectedTheme = string.Empty;

View File

@ -163,7 +163,10 @@
</data> </data>
<data name="KeyboardManager_EnableToggle.Header" xml:space="preserve"> <data name="KeyboardManager_EnableToggle.Header" xml:space="preserve">
<value>Enable Keyboard Manager</value> <value>Enable Keyboard Manager</value>
<comment>Keyboard Manager enable toggle header</comment> <comment>
Keyboard Manager enable toggle header
do not loc the Product name. Do you want this feature on / off
</comment>
</data> </data>
<data name="KeyboardManager_ProfileDescription.Text" xml:space="preserve"> <data name="KeyboardManager_ProfileDescription.Text" xml:space="preserve">
<value>Select the profile to display the active key remap and shortcuts</value> <value>Select the profile to display the active key remap and shortcuts</value>
@ -214,6 +217,7 @@
</data> </data>
<data name="ColorPicker_EnableColorPicker.Header" xml:space="preserve"> <data name="ColorPicker_EnableColorPicker.Header" xml:space="preserve">
<value>Enable Color Picker</value> <value>Enable Color Picker</value>
<comment>do not loc the Product name. Do you want this feature on / off</comment>
</data> </data>
<data name="ColorPicker_ChangeCursor.Content" xml:space="preserve"> <data name="ColorPicker_ChangeCursor.Content" xml:space="preserve">
<value>Change cursor when picking a color</value> <value>Change cursor when picking a color</value>
@ -229,6 +233,7 @@
</data> </data>
<data name="PowerLauncher_EnablePowerLauncher.Header" xml:space="preserve"> <data name="PowerLauncher_EnablePowerLauncher.Header" xml:space="preserve">
<value>Enable PowerToys Run</value> <value>Enable PowerToys Run</value>
<comment>do not loc the Product name. Do you want this feature on / off</comment>
</data> </data>
<data name="PowerLauncher_SearchResults.Text" xml:space="preserve"> <data name="PowerLauncher_SearchResults.Text" xml:space="preserve">
<value>Search &amp; results</value> <value>Search &amp; results</value>
@ -311,6 +316,7 @@
</data> </data>
<data name="FancyZones_EnableToggleControl_HeaderText.Header" xml:space="preserve"> <data name="FancyZones_EnableToggleControl_HeaderText.Header" xml:space="preserve">
<value>Enable FancyZones</value> <value>Enable FancyZones</value>
<comment>do not loc the Product name. Do you want this feature on / off</comment>
</data> </data>
<data name="FancyZones_ExcludeApps.Text" xml:space="preserve"> <data name="FancyZones_ExcludeApps.Text" xml:space="preserve">
<value>Excluded apps</value> <value>Excluded apps</value>
@ -373,10 +379,12 @@
<value>Give feedback</value> <value>Give feedback</value>
</data> </data>
<data name="Module_overview.Text" xml:space="preserve"> <data name="Module_overview.Text" xml:space="preserve">
<value>Module overview</value> <value>Learn more</value>
<comment>This label is there to point people to additional overview for how to use the product</comment>
</data> </data>
<data name="AttributionTitle.Text" xml:space="preserve"> <data name="AttributionTitle.Text" xml:space="preserve">
<value>Attribution</value> <value>Attribution</value>
<comment>giving credit to the projects this utility was based on</comment>
</data> </data>
<data name="GeneralPage_AboutPowerToysHeader.Text" xml:space="preserve"> <data name="GeneralPage_AboutPowerToysHeader.Text" xml:space="preserve">
<value>About PowerToys</value> <value>About PowerToys</value>
@ -392,12 +400,15 @@
</data> </data>
<data name="GeneralPage_ReportAbug.Text" xml:space="preserve"> <data name="GeneralPage_ReportAbug.Text" xml:space="preserve">
<value>Report a bug</value> <value>Report a bug</value>
<comment>Report an issue inside powertoys</comment>
</data> </data>
<data name="GeneralPage_RequestAFeature_URL.Text" xml:space="preserve"> <data name="GeneralPage_RequestAFeature_URL.Text" xml:space="preserve">
<value>Request a feature</value> <value>Request a feature</value>
<comment>Tell our team what we should build</comment>
</data> </data>
<data name="GeneralPage_RestartAsAdmin_Button.Content" xml:space="preserve"> <data name="GeneralPage_RestartAsAdmin_Button.Content" xml:space="preserve">
<value>Restart as administrator</value> <value>Restart as administrator</value>
<comment>running PowerToys as a higher level user, account is typically referred to as an admin / administrator</comment>
</data> </data>
<data name="GeneralPage_ToggleSwitch_RunAtStartUp.Header" xml:space="preserve"> <data name="GeneralPage_ToggleSwitch_RunAtStartUp.Header" xml:space="preserve">
<value>Run at startup</value> <value>Run at startup</value>
@ -407,9 +418,11 @@
</data> </data>
<data name="PowerRename_ShellIntegration.Text" xml:space="preserve"> <data name="PowerRename_ShellIntegration.Text" xml:space="preserve">
<value>Shell integration</value> <value>Shell integration</value>
<comment>This refers to directly integrating in with Windows</comment>
</data> </data>
<data name="PowerRename_Toggle_Enable.Header" xml:space="preserve"> <data name="PowerRename_Toggle_Enable.Header" xml:space="preserve">
<value>Enable PowerRename</value> <value>Enable PowerRename</value>
<comment>do not loc the Product name. Do you want this feature on / off</comment>
</data> </data>
<data name="RadioButtons_Name_Theme.Text" xml:space="preserve"> <data name="RadioButtons_Name_Theme.Text" xml:space="preserve">
<value>Settings theme</value> <value>Settings theme</value>
@ -428,12 +441,15 @@
</data> </data>
<data name="FileExplorerPreview_ToggleSwitch_Preview_MD.Header" xml:space="preserve"> <data name="FileExplorerPreview_ToggleSwitch_Preview_MD.Header" xml:space="preserve">
<value>Enable Markdown (.md) preview</value> <value>Enable Markdown (.md) preview</value>
<comment>Do not loc "Markdown". Do you want this feature on / off</comment>
</data> </data>
<data name="FileExplorerPreview_ToggleSwitch_Preview_SVG.Header" xml:space="preserve"> <data name="FileExplorerPreview_ToggleSwitch_Preview_SVG.Header" xml:space="preserve">
<value>Enable SVG (.svg) preview</value> <value>Enable SVG (.svg) preview</value>
<comment>Do you want this feature on / off</comment>
</data> </data>
<data name="FileExplorerPreview_ToggleSwitch_SVG_Thumbnail.Header" xml:space="preserve"> <data name="FileExplorerPreview_ToggleSwitch_SVG_Thumbnail.Header" xml:space="preserve">
<value>Enable SVG (.svg) thumbnails</value> <value>Enable SVG (.svg) thumbnails</value>
<comment>Do you want this feature on / off</comment>
</data> </data>
<data name="FileExplorerPreview_Description.Text" xml:space="preserve"> <data name="FileExplorerPreview_Description.Text" xml:space="preserve">
<value>These settings allow you to manage your Windows File Explorer custom preview handlers.</value> <value>These settings allow you to manage your Windows File Explorer custom preview handlers.</value>
@ -464,6 +480,7 @@
</data> </data>
<data name="ShortcutGuide_Enable.Header" xml:space="preserve"> <data name="ShortcutGuide_Enable.Header" xml:space="preserve">
<value>Enable Shortcut Guide</value> <value>Enable Shortcut Guide</value>
<comment>do not loc the Product name. Do you want this feature on / off</comment>
</data> </data>
<data name="ShortcutGuide_OverlayOpacity.Header" xml:space="preserve"> <data name="ShortcutGuide_OverlayOpacity.Header" xml:space="preserve">
<value>Opacity of background</value> <value>Opacity of background</value>
@ -476,6 +493,7 @@
</data> </data>
<data name="ImageResizer_EnableToggle.Header" xml:space="preserve"> <data name="ImageResizer_EnableToggle.Header" xml:space="preserve">
<value>Enable Image Resizer</value> <value>Enable Image Resizer</value>
<comment>do not loc the Product name. Do you want this feature on / off</comment>
</data> </data>
<data name="ImagesSizesListView.AutomationProperties.Name" xml:space="preserve"> <data name="ImagesSizesListView.AutomationProperties.Name" xml:space="preserve">
<value>Image Size</value> <value>Image Size</value>

View File

@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // 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;
using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Microsoft.PowerToys.Settings.UI.Library.ViewModels; using Microsoft.PowerToys.Settings.UI.Library.ViewModels;
@ -15,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
public ColorPickerPage() public ColorPickerPage()
{ {
var settingsUtils = new SettingsUtils(new SystemIOProvider()); var settingsUtils = new SettingsUtils();
ViewModel = new ColorPickerViewModel(settingsUtils, SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); ViewModel = new ColorPickerViewModel(settingsUtils, SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage);
DataContext = ViewModel; DataContext = ViewModel;
InitializeComponent(); InitializeComponent();

View File

@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
public FancyZonesPage() public FancyZonesPage()
{ {
InitializeComponent(); InitializeComponent();
var settingsUtils = new SettingsUtils(new SystemIOProvider()); var settingsUtils = new SettingsUtils();
ViewModel = new FancyZonesViewModel(SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), SettingsRepository<FancyZonesSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); ViewModel = new FancyZonesViewModel(SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), SettingsRepository<FancyZonesSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage);
DataContext = ViewModel; DataContext = ViewModel;
} }

View File

@ -36,7 +36,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
// Load string resources // Load string resources
ResourceLoader loader = ResourceLoader.GetForViewIndependentUse(); ResourceLoader loader = ResourceLoader.GetForViewIndependentUse();
var settingsUtils = new SettingsUtils(new SystemIOProvider()); var settingsUtils = new SettingsUtils();
ViewModel = new GeneralViewModel( ViewModel = new GeneralViewModel(
SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), SettingsRepository<GeneralSettings>.GetInstance(settingsUtils),

View File

@ -21,7 +21,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
public ImageResizerPage() public ImageResizerPage()
{ {
InitializeComponent(); InitializeComponent();
var settingsUtils = new SettingsUtils(new SystemIOProvider()); var settingsUtils = new SettingsUtils();
ViewModel = new ImageResizerViewModel(settingsUtils, SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); ViewModel = new ImageResizerViewModel(settingsUtils, SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage);
DataContext = ViewModel; DataContext = ViewModel;
} }

View File

@ -5,7 +5,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO.Abstractions;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Microsoft.PowerToys.Settings.UI.Library.ViewModels; using Microsoft.PowerToys.Settings.UI.Library.ViewModels;
@ -24,7 +24,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
private const string PowerToyName = "Keyboard Manager"; private const string PowerToyName = "Keyboard Manager";
private readonly CoreDispatcher dispatcher; private readonly CoreDispatcher dispatcher;
private readonly FileSystemWatcher watcher; private readonly IFileSystemWatcher watcher;
public KeyboardManagerViewModel ViewModel { get; } public KeyboardManagerViewModel ViewModel { get; }
@ -32,7 +32,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
{ {
dispatcher = Window.Current.Dispatcher; dispatcher = Window.Current.Dispatcher;
var settingsUtils = new SettingsUtils(new SystemIOProvider()); var settingsUtils = new SettingsUtils();
ViewModel = new KeyboardManagerViewModel(settingsUtils, SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, FilterRemapKeysList); ViewModel = new KeyboardManagerViewModel(settingsUtils, SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, FilterRemapKeysList);
watcher = Helper.GetFileWatcher( watcher = Helper.GetFileWatcher(

View File

@ -21,7 +21,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
public PowerLauncherPage() public PowerLauncherPage()
{ {
InitializeComponent(); InitializeComponent();
var settingsUtils = new SettingsUtils(new SystemIOProvider()); var settingsUtils = new SettingsUtils();
ViewModel = new PowerLauncherViewModel(settingsUtils, SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, (int)Windows.System.VirtualKey.Space); ViewModel = new PowerLauncherViewModel(settingsUtils, SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, (int)Windows.System.VirtualKey.Space);
DataContext = ViewModel; DataContext = ViewModel;

View File

@ -19,7 +19,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
public PowerPreviewPage() public PowerPreviewPage()
{ {
InitializeComponent(); InitializeComponent();
var settingsUtils = new SettingsUtils(new SystemIOProvider()); var settingsUtils = new SettingsUtils();
ViewModel = new PowerPreviewViewModel(SettingsRepository<PowerPreviewSettings>.GetInstance(settingsUtils), SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); ViewModel = new PowerPreviewViewModel(SettingsRepository<PowerPreviewSettings>.GetInstance(settingsUtils), SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage);
DataContext = ViewModel; DataContext = ViewModel;
} }

View File

@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
public PowerRenamePage() public PowerRenamePage()
{ {
InitializeComponent(); InitializeComponent();
var settingsUtils = new SettingsUtils(new SystemIOProvider()); var settingsUtils = new SettingsUtils();
ViewModel = new PowerRenameViewModel(settingsUtils, SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); ViewModel = new PowerRenameViewModel(settingsUtils, SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage);
DataContext = ViewModel; DataContext = ViewModel;

View File

@ -17,7 +17,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
{ {
InitializeComponent(); InitializeComponent();
var settingsUtils = new SettingsUtils(new SystemIOProvider()); var settingsUtils = new SettingsUtils();
ViewModel = new ShortcutGuideViewModel(SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), SettingsRepository<ShortcutGuideSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); ViewModel = new ShortcutGuideViewModel(SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), SettingsRepository<ShortcutGuideSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage);
DataContext = ViewModel; DataContext = ViewModel;
} }

View File

@ -148,6 +148,9 @@
<Item ItemId=";ColorModeHeader.Text" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ColorModeHeader.Text" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Choose a mode]]></Val> <Val><![CDATA[Choose a mode]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[モードの選択]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
@ -1483,33 +1486,6 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";PowerLauncher_Radio_Theme_Dark.Content" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Dark]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[ダーク]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";PowerLauncher_Radio_Theme_Default.Content" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[System default]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[システムの既定値]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";PowerLauncher_Radio_Theme_Light.Content" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Light]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[ライト]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";PowerLauncher_SearchResultPreference.Header" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";PowerLauncher_SearchResultPreference.Header" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Search result preference]]></Val> <Val><![CDATA[Search result preference]]></Val>
@ -1600,15 +1576,6 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";PowerLauncher_Theme.Text" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Choose color]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[色の選択]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";PowerRename_AutoCompleteHeader.Text" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";PowerRename_AutoCompleteHeader.Text" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Autocomplete]]></Val> <Val><![CDATA[Autocomplete]]></Val>
@ -1783,7 +1750,7 @@
<Item ItemId=";SettingsPage_SetShortcut.AutomationProperties.Name" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";SettingsPage_SetShortcut.AutomationProperties.Name" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Shortcut setting]]></Val> <Val><![CDATA[Shortcut setting]]></Val>
<Tgt Cat="Text" Stat="Update" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[ショートカットの設定]]></Val> <Val><![CDATA[ショートカットの設定]]></Val>
</Tgt> </Tgt>
<Prev Cat="Text"> <Prev Cat="Text">

View File

@ -148,6 +148,9 @@
<Item ItemId=";ColorModeHeader.Text" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ColorModeHeader.Text" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Choose a mode]]></Val> <Val><![CDATA[Choose a mode]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Välj ett läge]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
@ -1483,33 +1486,6 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";PowerLauncher_Radio_Theme_Dark.Content" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Dark]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Mörk]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";PowerLauncher_Radio_Theme_Default.Content" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[System default]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Systemstandard]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";PowerLauncher_Radio_Theme_Light.Content" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Light]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Ljus]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";PowerLauncher_SearchResultPreference.Header" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";PowerLauncher_SearchResultPreference.Header" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Search result preference]]></Val> <Val><![CDATA[Search result preference]]></Val>
@ -1600,12 +1576,6 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";PowerLauncher_Theme.Text" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Choose color]]></Val>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";PowerRename_AutoCompleteHeader.Text" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";PowerRename_AutoCompleteHeader.Text" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Autocomplete]]></Val> <Val><![CDATA[Autocomplete]]></Val>
@ -1736,7 +1706,7 @@
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Windows default]]></Val> <Val><![CDATA[Windows default]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Windows-standard]]></Val> <Val><![CDATA[Windows-standardinställning]]></Val>
</Tgt> </Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
@ -1780,8 +1750,8 @@
<Item ItemId=";SettingsPage_SetShortcut.AutomationProperties.Name" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";SettingsPage_SetShortcut.AutomationProperties.Name" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Shortcut setting]]></Val> <Val><![CDATA[Shortcut setting]]></Val>
<Tgt Cat="Text" Stat="Update" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Ange genväg]]></Val> <Val><![CDATA[Genvägsinställning]]></Val>
</Tgt> </Tgt>
<Prev Cat="Text"> <Prev Cat="Text">
<Val><![CDATA[Set Shortcut]]></Val> <Val><![CDATA[Set Shortcut]]></Val>

View File

@ -148,6 +148,9 @@
<Item ItemId=";ColorModeHeader.Text" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";ColorModeHeader.Text" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Choose a mode]]></Val> <Val><![CDATA[Choose a mode]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Mod seçin]]></Val>
</Tgt>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
@ -1483,33 +1486,6 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";PowerLauncher_Radio_Theme_Dark.Content" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Dark]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Koyu]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";PowerLauncher_Radio_Theme_Default.Content" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[System default]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Sistem varsayılanı]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";PowerLauncher_Radio_Theme_Light.Content" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Light]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Açık]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";PowerLauncher_SearchResultPreference.Header" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";PowerLauncher_SearchResultPreference.Header" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Search result preference]]></Val> <Val><![CDATA[Search result preference]]></Val>
@ -1600,12 +1576,6 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";PowerLauncher_Theme.Text" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Choose color]]></Val>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";PowerRename_AutoCompleteHeader.Text" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";PowerRename_AutoCompleteHeader.Text" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Autocomplete]]></Val> <Val><![CDATA[Autocomplete]]></Val>
@ -1780,8 +1750,8 @@
<Item ItemId=";SettingsPage_SetShortcut.AutomationProperties.Name" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";SettingsPage_SetShortcut.AutomationProperties.Name" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Shortcut setting]]></Val> <Val><![CDATA[Shortcut setting]]></Val>
<Tgt Cat="Text" Stat="Update" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Kısayolu Ayarla]]></Val> <Val><![CDATA[Kısayol ayarı]]></Val>
</Tgt> </Tgt>
<Prev Cat="Text"> <Prev Cat="Text">
<Val><![CDATA[Set Shortcut]]></Val> <Val><![CDATA[Set Shortcut]]></Val>

View File

@ -225,6 +225,9 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="System.IO.Abstractions">
<Version>12.2.5</Version>
</PackageReference>
<PackageReference Include="System.Windows.Interactivity.WPF"> <PackageReference Include="System.Windows.Interactivity.WPF">
<Version>2.0.20525</Version> <Version>2.0.20525</Version>
</PackageReference> </PackageReference>

View File

@ -66,6 +66,7 @@ namespace ColorPicker.Helpers
saturation = Math.Round(saturation * 100); saturation = Math.Round(saturation * 100);
lightness = Math.Round(lightness * 100); lightness = Math.Round(lightness * 100);
// Using InvariantCulture since this is used for color representation
return $"hsl({hue.ToString(CultureInfo.InvariantCulture)}" return $"hsl({hue.ToString(CultureInfo.InvariantCulture)}"
+ $", {saturation.ToString(CultureInfo.InvariantCulture)}%" + $", {saturation.ToString(CultureInfo.InvariantCulture)}%"
+ $", {lightness.ToString(CultureInfo.InvariantCulture)}%)"; + $", {lightness.ToString(CultureInfo.InvariantCulture)}%)";
@ -84,6 +85,7 @@ namespace ColorPicker.Helpers
saturation = Math.Round(saturation * 100); saturation = Math.Round(saturation * 100);
value = Math.Round(value * 100); value = Math.Round(value * 100);
// Using InvariantCulture since this is used for color representation
return $"hsv({hue.ToString(CultureInfo.InvariantCulture)}" return $"hsv({hue.ToString(CultureInfo.InvariantCulture)}"
+ $", {saturation.ToString(CultureInfo.InvariantCulture)}%" + $", {saturation.ToString(CultureInfo.InvariantCulture)}%"
+ $", {value.ToString(CultureInfo.InvariantCulture)}%)"; + $", {value.ToString(CultureInfo.InvariantCulture)}%)";
@ -103,6 +105,7 @@ namespace ColorPicker.Helpers
yellow = Math.Round(yellow * 100); yellow = Math.Round(yellow * 100);
blackKey = Math.Round(blackKey * 100); blackKey = Math.Round(blackKey * 100);
// Using InvariantCulture since this is used for color representation
return $"cmyk({cyan.ToString(CultureInfo.InvariantCulture)}%" return $"cmyk({cyan.ToString(CultureInfo.InvariantCulture)}%"
+ $", {magenta.ToString(CultureInfo.InvariantCulture)}%" + $", {magenta.ToString(CultureInfo.InvariantCulture)}%"
+ $", {yellow.ToString(CultureInfo.InvariantCulture)}%" + $", {yellow.ToString(CultureInfo.InvariantCulture)}%"

View File

@ -5,22 +5,24 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO.Abstractions;
namespace ColorPicker.Helpers namespace ColorPicker.Helpers
{ {
public static class Logger 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() 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)); Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));

View File

@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.IO; using System.IO.Abstractions;
using ColorPicker.Helpers; using ColorPicker.Helpers;
using Microsoft.Win32; 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")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Interop object")]
private const int SPIF_SENDCHANGE = 0x02; private const int SPIF_SENDCHANGE = 0x02;
private static readonly IFileSystem _fileSystem = new FileSystem();
public static void SetColorPickerCursor() public static void SetColorPickerCursor()
{ {
BackupOriginalCursors(); BackupOriginalCursors();
var colorPickerCursorPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ColorPickerCursorName); var colorPickerCursorPath = _fileSystem.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ColorPickerCursorName);
ChangeCursor(colorPickerCursorPath, ArrowRegistryName); ChangeCursor(colorPickerCursorPath, ArrowRegistryName);
ChangeCursor(colorPickerCursorPath, IBeamRegistryName); ChangeCursor(colorPickerCursorPath, IBeamRegistryName);
} }

View File

@ -5,6 +5,7 @@
using System; using System;
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using System.IO; using System.IO;
using System.IO.Abstractions;
using System.Threading; using System.Threading;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.Library.Utilities;
@ -20,14 +21,14 @@ namespace ColorPicker.Settings
private const int MaxNumberOfRetry = 5; private const int MaxNumberOfRetry = 5;
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Actually, call back is LoadSettingsFromJson")] [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(); private readonly object _loadingSettingsLock = new object();
[ImportingConstructor] [ImportingConstructor]
public UserSettings() public UserSettings()
{ {
_settingsUtils = new SettingsUtils(new SystemIOProvider()); _settingsUtils = new SettingsUtils();
ChangeCursor = new SettingItem<bool>(true); ChangeCursor = new SettingItem<bool>(true);
ActivationShortcut = new SettingItem<string>(DefaultActivationShortcut); ActivationShortcut = new SettingItem<string>(DefaultActivationShortcut);
CopiedColorRepresentation = new SettingItem<ColorRepresentationType>(ColorRepresentationType.HEX); CopiedColorRepresentation = new SettingItem<ColorRepresentationType>(ColorRepresentationType.HEX);

View File

@ -167,7 +167,7 @@ namespace UnitTest_ColorPickerUI.Helpers
{ {
var color = Color.FromArgb(red, green, blue); var color = Color.FromArgb(red, green, blue);
Exception? exception = null; Exception exception = null;
try try
{ {

View File

@ -2,20 +2,16 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>UnitTest_ColorPickerUI</RootNamespace>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<Nullable>enable</Nullable> <Platforms>x64</Platforms>
<LangVersion>8.0</LangVersion> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<OutputType>Library</OutputType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\..\..\..\x64\Debug\modules\ColorPicker\UnitTest-ColorPickerUI\</OutputPath> <OutputPath>..\..\..\..\x64\Debug\modules\ColorPicker\UnitTest-ColorPickerUI\</OutputPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\..\..\..\x64\Release\modules\ColorPicker\UnitTest-ColorPickerUI\</OutputPath> <OutputPath>..\..\..\..\x64\Release\modules\ColorPicker\UnitTest-ColorPickerUI\</OutputPath>
</PropertyGroup> </PropertyGroup>

View File

@ -6,6 +6,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.IO.Abstractions;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -41,6 +42,8 @@ namespace FancyZonesEditor
private const string CrashReportDynamicAssemblyTag = "dynamic assembly doesn't have location"; private const string CrashReportDynamicAssemblyTag = "dynamic assembly doesn't have location";
private const string CrashReportLocationNullTag = "location is null or empty"; private const string CrashReportLocationNullTag = "location is null or empty";
private readonly IFileSystem _fileSystem = new FileSystem();
public Settings ZoneSettings { get; } public Settings ZoneSettings { get; }
public App() public App()
@ -157,6 +160,8 @@ namespace FancyZonesEditor
sb.AppendLine("## " + CrashReportEnvironmentTag); sb.AppendLine("## " + CrashReportEnvironmentTag);
sb.AppendLine(CrashReportCommandLineTag + Environment.CommandLine); 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(CrashReportTimestampTag + DateTime.Now.ToString(CultureInfo.InvariantCulture));
sb.AppendLine(CrashReportOSVersionTag + Environment.OSVersion.VersionString); sb.AppendLine(CrashReportOSVersionTag + Environment.OSVersion.VersionString);
sb.AppendLine(CrashReportIntPtrLengthTag + IntPtr.Size); sb.AppendLine(CrashReportIntPtrLengthTag + IntPtr.Size);

View File

@ -250,6 +250,9 @@
<PackageReference Include="MahApps.Metro"> <PackageReference Include="MahApps.Metro">
<Version>2.3.0</Version> <Version>2.3.0</Version>
</PackageReference> </PackageReference>
<PackageReference Include="System.IO.Abstractions">
<Version>12.2.5</Version>
</PackageReference>
<PackageReference Include="System.Text.Json"> <PackageReference Include="System.Text.Json">
<Version>4.7.2</Version> <Version>4.7.2</Version>
</PackageReference> </PackageReference>

View File

@ -166,8 +166,8 @@
<TextBlock Name="windowEditorDialogTitle" Text="{x:Static props:Resources.Custom_Table_Layout}" Style="{StaticResource titleText}" /> <TextBlock Name="windowEditorDialogTitle" Text="{x:Static props:Resources.Custom_Table_Layout}" Style="{StaticResource titleText}" />
<TextBlock Text="{x:Static props:Resources.Note_Custom_Table}" Style="{StaticResource textLabel}" TextWrapping="Wrap" /> <TextBlock Text="{x:Static props:Resources.Note_Custom_Table}" Style="{StaticResource textLabel}" TextWrapping="Wrap" />
<TextBlock Text="{x:Static props:Resources.Name}" Style="{StaticResource textLabel}" /> <TextBlock x:Name="customLayoutName" Text="{x:Static props:Resources.Name}" Style="{StaticResource textLabel}" />
<TextBox Text="{Binding Name}" Style="{StaticResource textBox}" /> <TextBox Text="{Binding Name}" AutomationProperties.LabeledBy="{Binding ElementName=customLayoutName}" Style="{StaticResource textBox}" />
<!-- <!--
<StackPanel Orientation="Horizontal" Margin="0,8,0,0"> <StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<CheckBox x:Name="showGridSetting" VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="True" Margin="21,4,0,0"/> <CheckBox x:Name="showGridSetting" VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="True" Margin="21,4,0,0"/>

View File

@ -4,7 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO.Abstractions;
using System.Text.Json; using System.Text.Json;
using System.Windows; using System.Windows;
@ -201,7 +201,7 @@ namespace FancyZonesEditor.Models
try try
{ {
string jsonString = JsonSerializer.Serialize(jsonObj, options); string jsonString = JsonSerializer.Serialize(jsonObj, options);
File.WriteAllText(Settings.AppliedZoneSetTmpFile, jsonString); FileSystem.File.WriteAllText(Settings.AppliedZoneSetTmpFile, jsonString);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Text.Json; using System.Text.Json;
using System.Windows; using System.Windows;
@ -237,7 +236,7 @@ namespace FancyZonesEditor.Models
try try
{ {
string jsonString = JsonSerializer.Serialize(jsonObj, options); string jsonString = JsonSerializer.Serialize(jsonObj, options);
File.WriteAllText(Settings.AppliedZoneSetTmpFile, jsonString); FileSystem.File.WriteAllText(Settings.AppliedZoneSetTmpFile, jsonString);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.IO.Abstractions;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text.Json; using System.Text.Json;
@ -29,6 +30,8 @@ namespace FancyZonesEditor.Models
// Manages common properties and base persistence // Manages common properties and base persistence
public abstract class LayoutModel : INotifyPropertyChanged public abstract class LayoutModel : INotifyPropertyChanged
{ {
protected static readonly IFileSystem FileSystem = new FileSystem();
// Localizable strings // Localizable strings
private const string ErrorMessageBoxTitle = "FancyZones Editor Exception Handler"; private const string ErrorMessageBoxTitle = "FancyZones Editor Exception Handler";
private const string ErrorMessageBoxMessage = "Please report the bug to "; private const string ErrorMessageBoxMessage = "Please report the bug to ";
@ -194,7 +197,7 @@ namespace FancyZonesEditor.Models
try try
{ {
string jsonString = JsonSerializer.Serialize(deletedLayouts, options); string jsonString = JsonSerializer.Serialize(deletedLayouts, options);
File.WriteAllText(Settings.DeletedCustomZoneSetsTmpFile, jsonString); FileSystem.File.WriteAllText(Settings.DeletedCustomZoneSetsTmpFile, jsonString);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -209,7 +212,7 @@ namespace FancyZonesEditor.Models
try try
{ {
FileStream inputStream = File.Open(Settings.FancyZonesSettingsFile, FileMode.Open); Stream inputStream = FileSystem.File.Open(Settings.FancyZonesSettingsFile, FileMode.Open);
JsonDocument jsonObject = JsonDocument.Parse(inputStream, options: default); JsonDocument jsonObject = JsonDocument.Parse(inputStream, options: default);
JsonElement.ArrayEnumerator customZoneSetsEnumerator = jsonObject.RootElement.GetProperty(CustomZoneSetsJsonTag).EnumerateArray(); JsonElement.ArrayEnumerator customZoneSetsEnumerator = jsonObject.RootElement.GetProperty(CustomZoneSetsJsonTag).EnumerateArray();
@ -437,7 +440,7 @@ namespace FancyZonesEditor.Models
try try
{ {
string jsonString = JsonSerializer.Serialize(zoneSet, options); string jsonString = JsonSerializer.Serialize(zoneSet, options);
File.WriteAllText(Settings.ActiveZoneSetTmpFile, jsonString); FileSystem.File.WriteAllText(Settings.ActiveZoneSetTmpFile, jsonString);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.IO.Abstractions;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text.Json; using System.Text.Json;
using System.Windows; using System.Windows;
@ -39,6 +40,8 @@ namespace FancyZonesEditor
Debug, Debug,
} }
private static readonly IFileSystem _fileSystem = new FileSystem();
private static CanvasLayoutModel _blankCustomModel; private static CanvasLayoutModel _blankCustomModel;
private readonly CanvasLayoutModel _focusModel; private readonly CanvasLayoutModel _focusModel;
private readonly GridLayoutModel _rowsModel; private readonly GridLayoutModel _rowsModel;
@ -137,7 +140,7 @@ namespace FancyZonesEditor
public Settings() public Settings()
{ {
string tmpDirPath = Path.GetTempPath(); string tmpDirPath = _fileSystem.Path.GetTempPath();
ActiveZoneSetTmpFile = tmpDirPath + ActiveZoneSetsTmpFileName; ActiveZoneSetTmpFile = tmpDirPath + ActiveZoneSetsTmpFileName;
AppliedZoneSetTmpFile = tmpDirPath + AppliedZoneSetsTmpFileName; AppliedZoneSetTmpFile = tmpDirPath + AppliedZoneSetsTmpFileName;
@ -441,9 +444,9 @@ namespace FancyZonesEditor
ActiveZoneSetUUid = NullUuidStr; ActiveZoneSetUUid = NullUuidStr;
JsonElement jsonObject = default(JsonElement); JsonElement jsonObject = default(JsonElement);
if (File.Exists(Settings.ActiveZoneSetTmpFile)) if (_fileSystem.File.Exists(Settings.ActiveZoneSetTmpFile))
{ {
FileStream inputStream = File.Open(Settings.ActiveZoneSetTmpFile, FileMode.Open); Stream inputStream = _fileSystem.File.Open(Settings.ActiveZoneSetTmpFile, FileMode.Open);
jsonObject = JsonDocument.Parse(inputStream, options: default).RootElement; jsonObject = JsonDocument.Parse(inputStream, options: default).RootElement;
inputStream.Close(); inputStream.Close();
UniqueKey = jsonObject.GetProperty(DeviceIdJsonTag).GetString(); UniqueKey = jsonObject.GetProperty(DeviceIdJsonTag).GetString();

View File

@ -37,7 +37,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";FancyZones" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";FancyZones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[FancyZones]]></Val> <Val><![CDATA[FancyZones]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -46,7 +46,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";FancyZones_Data_Error" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";FancyZones_Data_Error" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[FancyZones persisted data path not found. Please report the bug to]]></Val> <Val><![CDATA[FancyZones persisted data path not found. Please report the bug to]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -55,7 +55,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";FancyZones_Editor_Launch_Error" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";FancyZones_Editor_Launch_Error" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[The FancyZones editor failed to start. Please report the bug to]]></Val> <Val><![CDATA[The FancyZones editor failed to start. Please report the bug to]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -64,7 +64,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";FancyZones_Settings_Load_Error" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";FancyZones_Settings_Load_Error" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Failed to load the FancyZones settings. Default settings will be used.]]></Val> <Val><![CDATA[Failed to load the FancyZones settings. Default settings will be used.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -73,7 +73,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";FancyZones_Settings_Save_Error" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";FancyZones_Settings_Save_Error" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Failed to save the FancyZones settings. Please retry again later, if the problem persists report the bug to]]></Val> <Val><![CDATA[Failed to save the FancyZones settings. Please retry again later, if the problem persists report the bug to]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -91,7 +91,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";Powertoys_FancyZones" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";Powertoys_FancyZones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[PowerToys - FancyZones]]></Val> <Val><![CDATA[PowerToys - FancyZones]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -100,7 +100,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";Setting_Description" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";Setting_Description" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Create window layouts to help make multi-tasking easy]]></Val> <Val><![CDATA[Create window layouts to help make multi-tasking easy]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -109,7 +109,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";Setting_Description_AppLastZone_MoveWindows" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";Setting_Description_AppLastZone_MoveWindows" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Move newly created windows to their last known zone]]></Val> <Val><![CDATA[Move newly created windows to their last known zone]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -118,7 +118,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";Setting_Description_DisplayChange_MoveWindows" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";Setting_Description_DisplayChange_MoveWindows" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Keep windows in their zones when the screen resolution changes]]></Val> <Val><![CDATA[Keep windows in their zones when the screen resolution changes]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -145,7 +145,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";Setting_Description_Move_Window_Across_Monitors" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";Setting_Description_Move_Window_Across_Monitors" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Move windows between zones across all monitors when snapping with (Win + Arrow)]]></Val> <Val><![CDATA[Move windows between zones across all monitors when snapping with (Win + Arrow)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -154,7 +154,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";Setting_Description_Move_Windows_Based_On_Position" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";Setting_Description_Move_Windows_Based_On_Position" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Move windows based on their position when snapping with (Win + Arrow)]]></Val> <Val><![CDATA[Move windows based on their position when snapping with (Win + Arrow)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -172,7 +172,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";Setting_Description_Override_Snap_Hotkeys" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";Setting_Description_Override_Snap_Hotkeys" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Override Windows Snap hotkeys (Win + Arrow) to move windows between zones]]></Val> <Val><![CDATA[Override Windows Snap hotkeys (Win + Arrow) to move windows between zones]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -190,7 +190,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";Setting_Description_ShiftDrag" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";Setting_Description_ShiftDrag" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Hold Shift key to activate zones while dragging]]></Val> <Val><![CDATA[Hold Shift key to activate zones while dragging]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -219,10 +219,13 @@
</Item> </Item>
<Item ItemId=";Setting_Description_Use_CursorPos_Editor_StartupScreen" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";Setting_Description_Use_CursorPos_Editor_StartupScreen" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Follow mouse cursor instead of focus when launching editor in a multi screen environment]]></Val> <Val><![CDATA[Follow mouse cursor instead of focus when launching editor in a multi-screen environment]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Při spouštění editoru v prostředí s více obrazovkami sledovat ukazatel myši místo fokusu]]></Val> <Val><![CDATA[Při spouštění editoru v prostředí s více obrazovkami sledovat ukazatel myši místo fokusu]]></Val>
</Tgt> </Tgt>
<Prev Cat="Text">
<Val><![CDATA[Follow mouse cursor instead of focus when launching editor in a multi screen environment]]></Val>
</Prev>
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
@ -244,7 +247,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";Setting_Description_ZoneSetChange_FlashZones" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";Setting_Description_ZoneSetChange_FlashZones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Flash zones when the active FancyZones layout changes]]></Val> <Val><![CDATA[Flash zones when the active FancyZones layout changes]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -253,7 +256,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";Setting_Description_ZoneSetChange_MoveWindows" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";Setting_Description_ZoneSetChange_MoveWindows" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[During zone layout changes, windows assigned to a zone will match new size/positions]]></Val> <Val><![CDATA[During zone layout changes, windows assigned to a zone will match new size/positions]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -271,7 +274,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";Setting_Excluded_Apps_Description" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";Setting_Excluded_Apps_Description" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[To exclude an application from snapping to zones add its name here (one per line). Excluded apps will react to the Windows Snap regardless of all other settings.]]></Val> <Val><![CDATA[To exclude an application from snapping to zones add its name here (one per line). Excluded apps will react to the Windows Snap regardless of all other settings.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -289,7 +292,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";Setting_Launch_Editor_Description" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";Setting_Launch_Editor_Description" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[To launch the zone editor, select the Edit zones button below or press the zone editor hotkey anytime]]></Val> <Val><![CDATA[To launch the zone editor, select the Edit zones button below or press the zone editor hotkey anytime]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -316,7 +319,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";Settings_Highlight_Opacity" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";Settings_Highlight_Opacity" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Zone opacity (%)]]></Val> <Val><![CDATA[Zone opacity (%)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">
@ -334,7 +337,7 @@
</Str> </Str>
<Disp Icon="Str" /> <Disp Icon="Str" />
</Item> </Item>
<Item ItemId=";Window_Event_Listener_Error" ItemType="0;.resx" PsrId="211" Leaf="true"> <Item ItemId=";Window_Event_Listener_Error" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text"> <Str Cat="Text">
<Val><![CDATA[Failed to install Windows event listener.]]></Val> <Val><![CDATA[Failed to install Windows event listener.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New"> <Tgt Cat="Text" Stat="Loc" Orig="New">

View File

@ -69,6 +69,7 @@
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118"> <PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="System.IO.Abstractions" Version="12.2.5" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> <PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">

View File

@ -5,7 +5,6 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;

View File

@ -170,6 +170,7 @@ namespace ImageResizer.Properties
var result = ((IDataErrorInfo)settings)["JpegQualityLevel"]; var result = ((IDataErrorInfo)settings)["JpegQualityLevel"];
// Using InvariantCulture since this is used internally
Assert.Equal( Assert.Equal(
string.Format(CultureInfo.InvariantCulture, Resources.ValueMustBeBetween, 1, 100), string.Format(CultureInfo.InvariantCulture, Resources.ValueMustBeBetween, 1, 100),
result); result);

View File

@ -6,7 +6,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO.Abstractions;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using Xunit; using Xunit;
@ -14,6 +14,8 @@ namespace ImageResizer.Test
{ {
internal static class AssertEx internal static class AssertEx
{ {
private static readonly IFileSystem _fileSystem = new FileSystem();
public static void All<T>(IEnumerable<T> collection, Action<T> action) public static void All<T>(IEnumerable<T> collection, Action<T> action)
{ {
foreach (var item in collection) foreach (var item in collection)
@ -24,7 +26,7 @@ namespace ImageResizer.Test
public static void Image(string path, Action<BitmapDecoder> action) public static void Image(string path, Action<BitmapDecoder> action)
{ {
using (var stream = File.OpenRead(path)) using (var stream = _fileSystem.File.OpenRead(path))
{ {
var image = BitmapDecoder.Create( var image = BitmapDecoder.Create(
stream, stream,

View File

@ -28,6 +28,7 @@ namespace ImageResizer.Views
var timeRemaining = new TimeSpan(hours, minutes, seconds); var timeRemaining = new TimeSpan(hours, minutes, seconds);
var converter = new TimeRemainingConverter(); var converter = new TimeRemainingConverter();
// Using InvariantCulture since these are internal
var result = converter.Convert( var result = converter.Convert(
timeRemaining, timeRemaining,
targetType: null, targetType: null,

View File

@ -69,6 +69,9 @@
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118"> <PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="System.IO.Abstractions">
<Version>12.2.5</Version>
</PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\common\interop\interop.vcxproj" /> <ProjectReference Include="..\..\..\common\interop\interop.vcxproj" />

View File

@ -6,6 +6,7 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Abstractions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using ImageResizer.Properties; using ImageResizer.Properties;
@ -14,6 +15,8 @@ namespace ImageResizer.Models
{ {
public class ResizeBatch public class ResizeBatch
{ {
private readonly IFileSystem _fileSystem = new FileSystem();
public string DestinationDirectory { get; set; } public string DestinationDirectory { get; set; }
public ICollection<string> Files { get; } = new List<string>(); public ICollection<string> Files { get; } = new List<string>();
@ -71,7 +74,7 @@ namespace ImageResizer.Models
catch (Exception ex) catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types #pragma warning restore CA1031 // Do not catch general exception types
{ {
errors.Add(new ResizeError { File = Path.GetFileName(file), Error = ex.Message }); errors.Add(new ResizeError { File = _fileSystem.Path.GetFileName(file), Error = ex.Message });
} }
Interlocked.Increment(ref completed); Interlocked.Increment(ref completed);

View File

@ -5,6 +5,7 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.IO.Abstractions;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Media; using System.Windows.Media;
@ -12,11 +13,14 @@ using System.Windows.Media.Imaging;
using ImageResizer.Properties; using ImageResizer.Properties;
using ImageResizer.Utilities; using ImageResizer.Utilities;
using Microsoft.VisualBasic.FileIO; using Microsoft.VisualBasic.FileIO;
using FileSystem = Microsoft.VisualBasic.FileIO.FileSystem;
namespace ImageResizer.Models namespace ImageResizer.Models
{ {
internal class ResizeOperation internal class ResizeOperation
{ {
private readonly IFileSystem _fileSystem = new System.IO.Abstractions.FileSystem();
private readonly string _file; private readonly string _file;
private readonly string _destinationDirectory; private readonly string _destinationDirectory;
private readonly Settings _settings; private readonly Settings _settings;
@ -31,7 +35,7 @@ namespace ImageResizer.Models
public void Execute() public void Execute()
{ {
string path; string path;
using (var inputStream = File.OpenRead(_file)) using (var inputStream = _fileSystem.File.OpenRead(_file))
{ {
var decoder = BitmapDecoder.Create( var decoder = BitmapDecoder.Create(
inputStream, inputStream,
@ -87,8 +91,8 @@ namespace ImageResizer.Models
} }
path = GetDestinationPath(encoder); path = GetDestinationPath(encoder);
Directory.CreateDirectory(Path.GetDirectoryName(path)); _fileSystem.Directory.CreateDirectory(_fileSystem.Path.GetDirectoryName(path));
using (var outputStream = File.Open(path, FileMode.CreateNew, FileAccess.Write)) using (var outputStream = _fileSystem.File.Open(path, FileMode.CreateNew, FileAccess.Write))
{ {
encoder.Save(outputStream); encoder.Save(outputStream);
} }
@ -96,13 +100,13 @@ namespace ImageResizer.Models
if (_settings.KeepDateModified) if (_settings.KeepDateModified)
{ {
File.SetLastWriteTimeUtc(path, File.GetLastWriteTimeUtc(_file)); _fileSystem.File.SetLastWriteTimeUtc(path, _fileSystem.File.GetLastWriteTimeUtc(_file));
} }
if (_settings.Replace) if (_settings.Replace)
{ {
var backup = GetBackupPath(); var backup = GetBackupPath();
File.Replace(path, _file, backup, ignoreMetadataErrors: true); _fileSystem.File.Replace(path, _file, backup, ignoreMetadataErrors: true);
FileSystem.DeleteFile(backup, UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin); FileSystem.DeleteFile(backup, UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin);
} }
} }
@ -179,16 +183,17 @@ namespace ImageResizer.Models
private string GetDestinationPath(BitmapEncoder encoder) private string GetDestinationPath(BitmapEncoder encoder)
{ {
var directory = _destinationDirectory ?? Path.GetDirectoryName(_file); var directory = _destinationDirectory ?? _fileSystem.Path.GetDirectoryName(_file);
var originalFileName = Path.GetFileNameWithoutExtension(_file); var originalFileName = _fileSystem.Path.GetFileNameWithoutExtension(_file);
var supportedExtensions = encoder.CodecInfo.FileExtensions.Split(','); var supportedExtensions = encoder.CodecInfo.FileExtensions.Split(',');
var extension = Path.GetExtension(_file); var extension = _fileSystem.Path.GetExtension(_file);
if (!supportedExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) if (!supportedExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{ {
extension = supportedExtensions.FirstOrDefault(); extension = supportedExtensions.FirstOrDefault();
} }
// Using CurrentCulture since this is user facing
var fileName = string.Format( var fileName = string.Format(
CultureInfo.CurrentCulture, CultureInfo.CurrentCulture,
_settings.FileNameFormat, _settings.FileNameFormat,
@ -198,11 +203,11 @@ namespace ImageResizer.Models
_settings.SelectedSize.Height, _settings.SelectedSize.Height,
encoder.Frames[0].PixelWidth, encoder.Frames[0].PixelWidth,
encoder.Frames[0].PixelHeight); encoder.Frames[0].PixelHeight);
var path = Path.Combine(directory, fileName + extension); var path = _fileSystem.Path.Combine(directory, fileName + extension);
var uniquifier = 1; var uniquifier = 1;
while (File.Exists(path)) while (_fileSystem.File.Exists(path))
{ {
path = Path.Combine(directory, fileName + " (" + uniquifier++ + ")" + extension); path = _fileSystem.Path.Combine(directory, fileName + " (" + uniquifier++ + ")" + extension);
} }
return path; return path;
@ -210,15 +215,15 @@ namespace ImageResizer.Models
private string GetBackupPath() private string GetBackupPath()
{ {
var directory = Path.GetDirectoryName(_file); var directory = _fileSystem.Path.GetDirectoryName(_file);
var fileName = Path.GetFileNameWithoutExtension(_file); var fileName = _fileSystem.Path.GetFileNameWithoutExtension(_file);
var extension = Path.GetExtension(_file); var extension = _fileSystem.Path.GetExtension(_file);
var path = Path.Combine(directory, fileName + ".bak" + extension); var path = _fileSystem.Path.Combine(directory, fileName + ".bak" + extension);
var uniquifier = 1; var uniquifier = 1;
while (File.Exists(path)) while (_fileSystem.File.Exists(path))
{ {
path = Path.Combine(directory, fileName + " (" + uniquifier++ + ")" + ".bak" + extension); path = _fileSystem.Path.Combine(directory, fileName + " (" + uniquifier++ + ")" + ".bak" + extension);
} }
return path; return path;

View File

@ -9,7 +9,7 @@ using System.Collections.ObjectModel;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO.Abstractions;
using System.Threading; using System.Threading;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using ImageResizer.Models; using ImageResizer.Models;
@ -22,9 +22,11 @@ namespace ImageResizer.Properties
[JsonObject(MemberSerialization.OptIn)] [JsonObject(MemberSerialization.OptIn)]
public sealed partial class Settings : IDataErrorInfo, INotifyPropertyChanged public sealed partial class Settings : IDataErrorInfo, INotifyPropertyChanged
{ {
private static readonly IFileSystem _fileSystem = new FileSystem();
// Used to synchronize access to the settings.json file // Used to synchronize access to the settings.json file
private static Mutex _jsonMutex = new Mutex(); private static Mutex _jsonMutex = new Mutex();
private static string _settingsPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), "Microsoft", "PowerToys", "ImageResizer", "settings.json"); private static string _settingsPath = _fileSystem.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), "Microsoft", "PowerToys", "ImageResizer", "settings.json");
private string _fileNameFormat; private string _fileNameFormat;
private bool _shrinkOnly; private bool _shrinkOnly;
private int _selectedSizeIndex; private int _selectedSizeIndex;
@ -63,6 +65,7 @@ namespace ImageResizer.Properties
public IEnumerable<ResizeSize> AllSizes { get; set; } public IEnumerable<ResizeSize> AllSizes { get; set; }
// Using OrdinalIgnoreCase since this is internal and used for comparison with symbols
public string FileNameFormat public string FileNameFormat
=> _fileNameFormat => _fileNameFormat
?? (_fileNameFormat = FileName ?? (_fileNameFormat = FileName
@ -111,6 +114,7 @@ namespace ImageResizer.Properties
if (JpegQualityLevel < 1 || JpegQualityLevel > 100) if (JpegQualityLevel < 1 || JpegQualityLevel > 100)
{ {
// Using CurrentCulture since this is user facing
return string.Format(CultureInfo.CurrentCulture, Resources.ValueMustBeBetween, 1, 100); return string.Format(CultureInfo.CurrentCulture, Resources.ValueMustBeBetween, 1, 100);
} }
@ -382,25 +386,25 @@ namespace ImageResizer.Properties
jsonData += "}"; jsonData += "}";
// Create directory if it doesn't exist // Create directory if it doesn't exist
FileInfo file = new FileInfo(SettingsPath); IFileInfo file = _fileSystem.FileInfo.FromFileName(SettingsPath);
file.Directory.Create(); file.Directory.Create();
// write string to file // write string to file
File.WriteAllText(SettingsPath, jsonData); _fileSystem.File.WriteAllText(SettingsPath, jsonData);
_jsonMutex.ReleaseMutex(); _jsonMutex.ReleaseMutex();
} }
public void Reload() public void Reload()
{ {
_jsonMutex.WaitOne(); _jsonMutex.WaitOne();
if (!File.Exists(SettingsPath)) if (!_fileSystem.File.Exists(SettingsPath))
{ {
_jsonMutex.ReleaseMutex(); _jsonMutex.ReleaseMutex();
Save(); Save();
return; return;
} }
string jsonData = File.ReadAllText(SettingsPath); string jsonData = _fileSystem.File.ReadAllText(SettingsPath);
JObject imageResizerSettings = JObject.Parse(jsonData); JObject imageResizerSettings = JObject.Parse(jsonData);
// Replace the { "value": <Value> } with <Value> to match the Settings object format // Replace the { "value": <Value> } with <Value> to match the Settings object format

View File

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Documents;
namespace ImageResizer.Views
{
public class AccessibleHyperlink : Hyperlink
{
public AutomationControlType ControlType { get; set; }
protected override AutomationPeer OnCreateAutomationPeer()
{
var peer = new CustomizableHyperlinkAutomationPeer(this);
peer.ControlType = ControlType;
return peer;
}
}
}

View File

@ -114,18 +114,18 @@
</ComboBox.ItemTemplate> </ComboBox.ItemTemplate>
</ComboBox> </ComboBox>
<TextBlock Grid.Column="6" Margin="5,0,0,0" VerticalAlignment="Center"> <TextBlock Grid.Column="6" Margin="5,0,0,0" VerticalAlignment="Center">
<Hyperlink Command="{Binding DataContext.RemoveSizeCommand,ElementName=_this}" CommandParameter="{Binding}"> <local:AccessibleHyperlink ControlType="Button" Command="{Binding DataContext.RemoveSizeCommand,ElementName=_this}" CommandParameter="{Binding}">
<Run Text="{x:Static p:Resources.Advanced_DeleteSize}"/> <Run Text="{x:Static p:Resources.Advanced_DeleteSize}"/>
</Hyperlink> </local:AccessibleHyperlink>
</TextBlock> </TextBlock>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
<TextBlock> <TextBlock>
<Hyperlink Command="{Binding AddSizeCommand}"> <local:AccessibleHyperlink ControlType="Button" Command="{Binding AddSizeCommand}">
<Run Text="{x:Static p:Resources.Advanced_CreateSize}"/> <Run Text="{x:Static p:Resources.Advanced_CreateSize}"/>
</Hyperlink> </local:AccessibleHyperlink>
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>
</TabItem> </TabItem>

View File

@ -0,0 +1,24 @@
// 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.Windows.Automation.Peers;
using System.Windows.Documents;
namespace ImageResizer.Views
{
public class CustomizableHyperlinkAutomationPeer : HyperlinkAutomationPeer
{
public CustomizableHyperlinkAutomationPeer(Hyperlink owner)
: base(owner)
{
}
public AutomationControlType ControlType { get; set; }
protected override AutomationControlType GetAutomationControlTypeCore()
{
return ControlType;
}
}
}

Some files were not shown because too many files have changed in this diff Show More