Merge branch 'master' into users/niels9001/settings-accesbilitynarratorsupportforshortcutcontrol

This commit is contained in:
Niels Laute
2020-10-23 16:16:09 +02:00
committed by GitHub
446 changed files with 7966 additions and 4126 deletions

View File

@@ -45,3 +45,6 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.ThrowLastWin32Error(System.String)", Scope = "member", Target = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.#InternalGetTarget(Microsoft.Win32.SafeHandles.SafeFileHandle)", Justification = "Only used for local generation")]
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.ThrowLastWin32Error(System.String)", Scope = "member", Target = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.#OpenReparsePoint(System.String,Microsoft.Templates.Core.Locations.JunctionNativeMethods+EFileAccess)", Justification = "Only used for local generation")]
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Windows.Documents.InlineCollection.Add(System.String)", Scope = "member", Target = "Microsoft.Templates.UI.Extensions.TextBlockExtensions.#OnSequentialFlowStepChanged(System.Windows.DependencyObject,System.Windows.DependencyPropertyChangedEventArgs)", Justification = "No text here")]
// FxCop warning suppression for uninstantiated TestFixture classes
[assembly: SuppressMessage("Microsoft.Performance", "CA1812: Avoid uninstantiated internal classes", Scope = "module", Justification = "CA1812 will be thrown for every file in the test project. This is mentioned here: dotnet/roslyn-analyzers#1830")]

View File

@@ -53,11 +53,12 @@ namespace UnitTestsCommonLib
private:
const std::wstring m_json = L"{\"name\":\"Module Name\",\"properties\" : {\"bool_toggle_true\":{\"value\":true},\"bool_toggle_false\":{\"value\":false},\"color_picker\" : {\"value\":\"#ff8d12\"},\"int_spinner\" : {\"value\":10},\"string_text\" : {\"value\":\"a quick fox\"}},\"version\" : \"1.0\" }";
const std::wstring m_moduleName = L"Module Name";
const std::wstring m_moduleKey = L"Module Key";
public:
TEST_METHOD (LoadFromJsonBoolTrue)
{
PowerToyValues values = PowerToyValues::from_json_string(m_json);
PowerToyValues values = PowerToyValues::from_json_string(m_json, m_moduleKey);
auto value = values.get_bool_value(L"bool_toggle_true");
Assert::IsTrue(value.has_value());
Assert::AreEqual(true, *value);
@@ -65,7 +66,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (LoadFromJsonBoolFalse)
{
PowerToyValues values = PowerToyValues::from_json_string(m_json);
PowerToyValues values = PowerToyValues::from_json_string(m_json, m_moduleKey);
auto value = values.get_bool_value(L"bool_toggle_false");
Assert::IsTrue(value.has_value());
Assert::AreEqual(false, *value);
@@ -73,7 +74,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (LoadFromJsonInt)
{
PowerToyValues values = PowerToyValues::from_json_string(m_json);
PowerToyValues values = PowerToyValues::from_json_string(m_json, m_moduleKey);
auto value = values.get_int_value(L"int_spinner");
Assert::IsTrue(value.has_value());
Assert::AreEqual(10, *value);
@@ -81,7 +82,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (LoadFromJsonString)
{
PowerToyValues values = PowerToyValues::from_json_string(m_json);
PowerToyValues values = PowerToyValues::from_json_string(m_json, m_moduleKey);
auto value = values.get_string_value(L"string_text");
Assert::IsTrue(value.has_value());
@@ -91,7 +92,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (LoadFromJsonColorPicker)
{
PowerToyValues values = PowerToyValues::from_json_string(m_json);
PowerToyValues values = PowerToyValues::from_json_string(m_json, m_moduleKey);
auto value = values.get_string_value(L"color_picker");
Assert::IsTrue(value.has_value());
@@ -101,19 +102,19 @@ namespace UnitTestsCommonLib
TEST_METHOD (LoadFromEmptyString)
{
auto func = [] { PowerToyValues values = PowerToyValues::from_json_string(L""); };
auto func = [] { PowerToyValues values = PowerToyValues::from_json_string(L"", L"Module Key"); };
Assert::ExpectException<winrt::hresult_error>(func);
}
TEST_METHOD (LoadFromInvalidString_NameMissed)
{
auto func = [] { PowerToyValues values = PowerToyValues::from_json_string(L"{\"properties\" : {\"bool_toggle_true\":{\"value\":true},\"bool_toggle_false\":{\"value\":false},\"color_picker\" : {\"value\":\"#ff8d12\"},\"int_spinner\" : {\"value\":10},\"string_text\" : {\"value\":\"a quick fox\"}},\"version\" : \"1.0\" }"); };
auto func = [] { PowerToyValues values = PowerToyValues::from_json_string(L"{\"properties\" : {\"bool_toggle_true\":{\"value\":true},\"bool_toggle_false\":{\"value\":false},\"color_picker\" : {\"value\":\"#ff8d12\"},\"int_spinner\" : {\"value\":10},\"string_text\" : {\"value\":\"a quick fox\"}},\"version\" : \"1.0\" }", L"Module Key"); };
Assert::ExpectException<winrt::hresult_error>(func);
}
TEST_METHOD (LoadFromInvalidString_VersionMissed)
{
PowerToyValues values = PowerToyValues::from_json_string(L"{\"name\":\"Module Name\",\"properties\" : {}}");
PowerToyValues values = PowerToyValues::from_json_string(L"{\"name\":\"Module Name\",\"properties\" : {}}", L"Module Key");
const std::wstring expectedStr = L"{\"name\" : \"Module Name\", \"properties\" : {},\"version\" : \"1.0\"}";
const auto expected = json::JsonObject::Parse(expectedStr);
const auto actual = json::JsonObject::Parse(values.serialize());
@@ -123,7 +124,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (LoadFromInvalidString_PropertiesMissed)
{
PowerToyValues values = PowerToyValues::from_json_string(L"{\"name\":\"Module Name\",\"version\" : \"1.0\" }");
PowerToyValues values = PowerToyValues::from_json_string(L"{\"name\":\"Module Name\",\"version\" : \"1.0\" }", L"Module Key");
const std::wstring expectedStr = L"{\"name\":\"Module Name\",\"version\" : \"1.0\" }";
const auto expected = json::JsonObject::Parse(expectedStr);
const auto actual = json::JsonObject::Parse(values.serialize());
@@ -133,7 +134,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (LoadFromValidString_EmptyProperties)
{
PowerToyValues values = PowerToyValues::from_json_string(L"{\"name\":\"Module Name\",\"properties\" : {}, \"version\" : \"1.0\" }");
PowerToyValues values = PowerToyValues::from_json_string(L"{\"name\":\"Module Name\",\"properties\" : {}, \"version\" : \"1.0\" }", L"Module Key");
const std::wstring expectedStr = L"{\"name\":\"Module Name\",\"properties\" : {},\"version\" : \"1.0\" }";
const auto expected = json::JsonObject::Parse(expectedStr);
const auto actual = json::JsonObject::Parse(values.serialize());
@@ -143,7 +144,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (LoadFromValidString_ChangedVersion)
{
PowerToyValues values = PowerToyValues::from_json_string(L"{\"name\":\"Module Name\",\"properties\" : {},\"version\" : \"2.0\"}");
PowerToyValues values = PowerToyValues::from_json_string(L"{\"name\":\"Module Name\",\"properties\" : {},\"version\" : \"2.0\"}", L"Module Key");
const std::wstring expectedStr = L"{\"name\" : \"Module Name\", \"properties\" : {},\"version\" : \"1.0\"}"; //version from input json is ignored
const auto expected = json::JsonObject::Parse(expectedStr);
@@ -154,7 +155,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (CreateWithName)
{
PowerToyValues values(m_moduleName);
PowerToyValues values(m_moduleName, m_moduleKey);
const std::wstring expectedStr = L"{\"name\":\"Module Name\",\"properties\" : {},\"version\" : \"1.0\" }";
const auto expected = json::JsonObject::Parse(expectedStr);
@@ -165,7 +166,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (AddPropertyBoolPositive)
{
PowerToyValues values(m_moduleName);
PowerToyValues values(m_moduleName, m_moduleKey);
values.add_property<bool>(L"positive_bool_value", true);
auto value = values.get_bool_value(L"positive_bool_value");
@@ -175,7 +176,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (AddPropertyBoolNegative)
{
PowerToyValues values(m_moduleName);
PowerToyValues values(m_moduleName, m_moduleKey);
values.add_property<bool>(L"negative_bool_value", false);
auto value = values.get_bool_value(L"negative_bool_value");
@@ -185,7 +186,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (AddPropertyIntPositive)
{
PowerToyValues values(m_moduleName);
PowerToyValues values(m_moduleName, m_moduleKey);
const int intVal = 4392854;
values.add_property<int>(L"integer", intVal);
@@ -196,7 +197,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (AddPropertyIntNegative)
{
PowerToyValues values(m_moduleName);
PowerToyValues values(m_moduleName, m_moduleKey);
const int intVal = -4392854;
values.add_property<int>(L"integer", intVal);
@@ -207,7 +208,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (AddPropertyIntZero)
{
PowerToyValues values(m_moduleName);
PowerToyValues values(m_moduleName, m_moduleKey);
const int intVal = 0;
values.add_property<int>(L"integer", intVal);
@@ -218,7 +219,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (AddPropertyStringEmpty)
{
PowerToyValues values(m_moduleName);
PowerToyValues values(m_moduleName, m_moduleKey);
const std::wstring stringVal = L"";
values.add_property<std::wstring>(L"stringval", stringVal);
@@ -229,7 +230,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (AddPropertyString)
{
PowerToyValues values(m_moduleName);
PowerToyValues values(m_moduleName, m_moduleKey);
const std::wstring stringVal = L"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
values.add_property<std::wstring>(L"stringval", stringVal);
@@ -240,7 +241,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (AddPropertyJsonEmpty)
{
PowerToyValues values(m_moduleName);
PowerToyValues values(m_moduleName, m_moduleKey);
const auto json = json::JsonObject();
values.add_property<json::JsonObject>(L"jsonval", json);
@@ -251,7 +252,7 @@ namespace UnitTestsCommonLib
TEST_METHOD (AddPropertyJsonObject)
{
PowerToyValues values(m_moduleName);
PowerToyValues values(m_moduleName, m_moduleKey);
const auto json = json::JsonObject::Parse(m_json);
values.add_property<json::JsonObject>(L"jsonval", json);

View File

@@ -114,12 +114,12 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.200519.2" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.200902.2" targetFramework="native" />
</packages>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
<Import Project="..\Version.props" />
@@ -140,6 +140,7 @@
<ClInclude Include="shared_constants.h" />
<ClInclude Include="string_utils.h" />
<ClInclude Include="timeutil.h" />
<ClInclude Include="toast_dont_show_again.h" />
<ClInclude Include="two_way_pipe_message_ipc.h" />
<ClInclude Include="VersionHelper.h" />
<ClInclude Include="window_helpers.h" />
@@ -185,6 +186,7 @@
<ClCompile Include="start_visible.cpp" />
<ClCompile Include="tasklist_positions.cpp" />
<ClCompile Include="common.cpp" />
<ClCompile Include="toast_dont_show_again.cpp" />
<ClCompile Include="version.cpp" />
<ClCompile Include="two_way_pipe_message_ipc.cpp" />
<ClCompile Include="VersionHelper.cpp" />
@@ -197,15 +199,15 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>

View File

@@ -141,6 +141,9 @@
<ClInclude Include="string_utils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="toast_dont_show_again.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="d2d_svg.cpp">
@@ -222,6 +225,9 @@
<ClCompile Include="comUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="toast_dont_show_again.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -1,5 +1,7 @@
#pragma once
#include <cinttypes>
using namespace System::Threading;
using namespace System::Collections::Generic;
@@ -10,7 +12,7 @@ public
{
WPARAM message;
int key;
DWORD dwExtraInfo;
uint64_t dwExtraInfo;
};
public

View File

@@ -98,6 +98,8 @@
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<MultiProcessorCompilation Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</MultiProcessorCompilation>
<MultiProcessorCompilation Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</MultiProcessorCompilation>
<TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</TreatWarningAsError>
<TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</TreatWarningAsError>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>

View File

@@ -27,7 +27,7 @@ std::vector<DWORD> LayoutMap::GetKeyCodeList(const bool isShortcut)
return impl->GetKeyCodeList(isShortcut);
}
std::vector<std::wstring> LayoutMap::GetKeyNameList(const bool isShortcut)
std::vector<std::pair<DWORD, std::wstring>> LayoutMap::GetKeyNameList(const bool isShortcut)
{
return impl->GetKeyNameList(isShortcut);
}
@@ -305,25 +305,25 @@ std::vector<DWORD> LayoutMap::LayoutMapImpl::GetKeyCodeList(const bool isShortcu
return keyCodes;
}
std::vector<std::wstring> LayoutMap::LayoutMapImpl::GetKeyNameList(const bool isShortcut)
std::vector<std::pair<DWORD, std::wstring>> LayoutMap::LayoutMapImpl::GetKeyNameList(const bool isShortcut)
{
std::vector<std::wstring> keyNames;
std::vector<std::pair<DWORD, std::wstring>> keyNames;
std::vector<DWORD> keyCodes = GetKeyCodeList(isShortcut);
std::lock_guard<std::mutex> lock(keyboardLayoutMap_mutex);
// If it is a key list for the shortcut control then we add a "None" key at the start
if (isShortcut)
{
keyNames.push_back(L"None");
keyNames.push_back({ 0, L"None" });
for (int i = 1; i < keyCodes.size(); i++)
{
keyNames.push_back(keyboardLayoutMap[keyCodes[i]]);
keyNames.push_back({ keyCodes[i], keyboardLayoutMap[keyCodes[i]] });
}
}
else
{
for (int i = 0; i < keyCodes.size(); i++)
{
keyNames.push_back(keyboardLayoutMap[keyCodes[i]]);
keyNames.push_back({ keyCodes[i], keyboardLayoutMap[keyCodes[i]] });
}
}

View File

@@ -12,7 +12,7 @@ public:
void UpdateLayout();
std::wstring GetKeyName(DWORD key);
std::vector<DWORD> GetKeyCodeList(const bool isShortcut = false);
std::vector<std::wstring> GetKeyNameList(const bool isShortcut = false);
std::vector<std::pair<DWORD, std::wstring>> GetKeyNameList(const bool isShortcut = false);
private:
class LayoutMapImpl;

View File

@@ -46,6 +46,6 @@ public:
// Function to return the list of key codes in the order for the drop down. It creates it if it doesn't exist
std::vector<DWORD> GetKeyCodeList(const bool isShortcut);
// Function to return the list of key name in the order for the drop down based on the key codes
std::vector<std::wstring> GetKeyNameList(const bool isShortcut);
// Function to return the list of key name pairs in the order for the drop down based on the key codes
std::vector<std::pair<DWORD, std::wstring>> GetKeyNameList(const bool isShortcut);
};

View File

@@ -11,7 +11,10 @@
#include <winrt/Windows.Data.Xml.Dom.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.UI.Notifications.h>
#include <winrt/Windows.UI.Notifications.Management.h>
#include <winrt/Windows.ApplicationModel.Background.h>
#include <winrt/Windows.System.h>
#include <winrt/Windows.System.UserProfile.h>
#include <wil/com.h>
#include <propvarutil.h>
#include <propkey.h>
@@ -39,6 +42,7 @@ namespace
constexpr std::wstring_view APPIDS_REGISTRY = LR"(Software\Classes\AppUserModelId\)";
std::wstring APPLICATION_ID = L"Microsoft.PowerToysWin32";
constexpr std::wstring_view DEFAULT_TOAST_GROUP = L"PowerToysToastTag";
}
namespace localized_strings
@@ -275,11 +279,10 @@ void notifications::show_toast_with_activations(std::wstring message,
std::wstring toast_xml;
toast_xml.reserve(2048);
toast_xml += LR"(<?xml version="1.0"?><toast><visual><binding template="ToastGeneric"><text id="1">)";
toast_xml += title;
toast_xml += LR"(</text><text id="2">)";
toast_xml += message;
toast_xml += L"</text>";
toast_xml += LR"(<?xml version="1.0"?><toast><visual><binding template="ToastGeneric">)";
toast_xml += LR"(<text id="1">{title}</text>)";
toast_xml += LR"(<text id="2">{message}</text>)";
if (params.progress_bar.has_value())
{
toast_xml += LR"(<progress title="{progressTitle}" value="{progressValue}" valueStringOverride="{progressValueString}" status="" />)";
@@ -373,17 +376,20 @@ void notifications::show_toast_with_activations(std::wstring message,
xml_escape(toast_xml);
toast_xml_doc.LoadXml(toast_xml);
ToastNotification notification{ toast_xml_doc };
notification.Group(DEFAULT_TOAST_GROUP);
winrt::Windows::Foundation::Collections::StringMap map;
map.Insert(L"message", std::move(message));
map.Insert(L"title", std::move(title));
if (params.progress_bar.has_value())
{
float progress = std::clamp(params.progress_bar->progress, 0.0f, 1.0f);
winrt::Windows::Foundation::Collections::StringMap map;
map.Insert(L"progressValue", std::to_wstring(progress));
map.Insert(L"progressValueString", std::to_wstring(static_cast<int>(progress * 100)) + std::wstring(L"%"));
map.Insert(L"progressTitle", params.progress_bar->progress_title);
winrt::Windows::UI::Notifications::NotificationData data(map);
notification.Data(data);
}
winrt::Windows::UI::Notifications::NotificationData data{ map };
notification.Data(std::move(data));
const auto notifier = winstore::running_as_packaged() ? ToastNotificationManager::ToastNotificationManager::CreateToastNotifier() :
ToastNotificationManager::ToastNotificationManager::CreateToastNotifier(APPLICATION_ID);
@@ -412,7 +418,7 @@ void notifications::show_toast_with_activations(std::wstring message,
}
}
void notifications::update_progress_bar_toast(std::wstring_view tag, progress_bar_params params)
void notifications::update_toast_progress_bar(std::wstring_view tag, progress_bar_params params)
{
const auto notifier = winstore::running_as_packaged() ?
ToastNotificationManager::ToastNotificationManager::CreateToastNotifier() :
@@ -425,5 +431,41 @@ void notifications::update_progress_bar_toast(std::wstring_view tag, progress_ba
map.Insert(L"progressTitle", params.progress_title);
winrt::Windows::UI::Notifications::NotificationData data(map);
winrt::Windows::UI::Notifications::NotificationUpdateResult res = notifier.Update(data, tag);
winrt::Windows::UI::Notifications::NotificationUpdateResult res = notifier.Update(data, tag, DEFAULT_TOAST_GROUP);
}
void notifications::update_toast_contents(std::wstring_view tag, std::wstring plaintext_message, std::wstring title)
{
const auto notifier = winstore::running_as_packaged() ?
ToastNotificationManager::ToastNotificationManager::CreateToastNotifier() :
ToastNotificationManager::ToastNotificationManager::CreateToastNotifier(APPLICATION_ID);
winrt::Windows::Foundation::Collections::StringMap map;
map.Insert(L"title", std::move(title));
map.Insert(L"message", std::move(plaintext_message));
winrt::Windows::UI::Notifications::NotificationData data(map);
winrt::Windows::UI::Notifications::NotificationUpdateResult res = notifier.Update(data, tag, DEFAULT_TOAST_GROUP);
}
void notifications::remove_toasts(std::wstring_view tag)
{
using namespace winrt::Windows::System;
try
{
User currentUser{ *User::FindAllAsync(UserType::LocalUser, UserAuthenticationStatus::LocallyAuthenticated).get().First() };
if (!currentUser)
{
return;
}
currentUser.GetPropertyAsync(KnownUserProperties::AccountName());
auto toastHistory = ToastNotificationManager::GetForUser(currentUser).History();
toastHistory.Remove(tag, DEFAULT_TOAST_GROUP, APPLICATION_ID);
}
catch (...)
{
// Couldn't get the current user or problem removing the toast => nothing we can do
}
}

View File

@@ -9,6 +9,7 @@
namespace notifications
{
constexpr inline const wchar_t TOAST_ACTIVATED_LAUNCH_ARG[] = L"-ToastActivated";
constexpr inline const wchar_t UPDATING_PROCESS_TOAST_TAG[] = L"PTUpdateNotifyTag";
void override_application_id(const std::wstring_view appID);
void register_background_toast_handler();
@@ -59,5 +60,7 @@ namespace notifications
void show_toast(std::wstring plaintext_message, std::wstring title, toast_params params = {});
void show_toast_with_activations(std::wstring plaintext_message, std::wstring title, std::wstring_view background_handler_id, std::vector<action_t> actions, toast_params params = {});
void update_progress_bar_toast(std::wstring_view tag, progress_bar_params params);
void update_toast_progress_bar(std::wstring_view tag, progress_bar_params params);
void update_toast_contents(std::wstring_view tag, std::wstring plaintext_message, std::wstring title);
void remove_toasts(std::wstring_view tag);
}

View File

@@ -1,71 +0,0 @@
#pragma once
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#include <limits>
#include "../timeutil.h"
namespace
{
const inline wchar_t CANT_DRAG_ELEVATED_DONT_SHOW_AGAIN_REGISTRY_PATH[] = LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain\{e16ea82f-6d94-4f30-bb02-d6d911588afd})";
const inline int64_t disable_interval_in_days = 30;
}
inline bool disable_cant_drag_elevated_warning()
{
HKEY key{};
if (RegCreateKeyExW(HKEY_CURRENT_USER,
CANT_DRAG_ELEVATED_DONT_SHOW_AGAIN_REGISTRY_PATH,
0,
nullptr,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
nullptr,
&key,
nullptr) != ERROR_SUCCESS)
{
return false;
}
const auto now = timeutil::now();
const size_t buf_size = sizeof(now);
if (RegSetValueExW(key, nullptr, 0, REG_QWORD, reinterpret_cast<const BYTE*>(&now), sizeof(now)) != ERROR_SUCCESS)
{
RegCloseKey(key);
return false;
}
RegCloseKey(key);
return true;
}
inline bool is_cant_drag_elevated_warning_disabled()
{
HKEY key{};
if (RegOpenKeyExW(HKEY_CURRENT_USER,
CANT_DRAG_ELEVATED_DONT_SHOW_AGAIN_REGISTRY_PATH,
0,
KEY_READ,
&key) != ERROR_SUCCESS)
{
return false;
}
std::wstring buffer(std::numeric_limits<time_t>::digits10 + 2, L'\0');
time_t last_disabled_time{};
DWORD time_size = static_cast<DWORD>(sizeof(last_disabled_time));
if (RegGetValueW(
key,
nullptr,
nullptr,
RRF_RT_REG_QWORD,
nullptr,
&last_disabled_time,
&time_size) != ERROR_SUCCESS)
{
RegCloseKey(key);
return false;
}
RegCloseKey(key);
return timeutil::diff::in_days(timeutil::now(), last_disabled_time) < disable_interval_in_days;
return false;
}

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.200519.2" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.200902.2" targetFramework="native" />
</packages>

View File

@@ -23,11 +23,11 @@ namespace PTSettingsHelper
return result;
}
std::wstring get_module_save_folder_location(std::wstring_view powertoy_name)
std::wstring get_module_save_folder_location(std::wstring_view powertoy_key)
{
std::wstring result = get_root_save_folder_location();
result += L"\\";
result += powertoy_name;
result += powertoy_key;
std::filesystem::path save_path(result);
if (!std::filesystem::exists(save_path))
{
@@ -36,9 +36,9 @@ namespace PTSettingsHelper
return result;
}
std::wstring get_module_save_file_location(std::wstring_view powertoy_name)
std::wstring get_module_save_file_location(std::wstring_view powertoy_key)
{
return get_module_save_folder_location(powertoy_name) + settings_filename;
return get_module_save_folder_location(powertoy_key) + settings_filename;
}
std::wstring get_powertoys_general_save_file_location()
@@ -46,15 +46,15 @@ namespace PTSettingsHelper
return get_root_save_folder_location() + settings_filename;
}
void save_module_settings(std::wstring_view powertoy_name, json::JsonObject& settings)
void save_module_settings(std::wstring_view powertoy_key, json::JsonObject& settings)
{
const std::wstring save_file_location = get_module_save_file_location(powertoy_name);
const std::wstring save_file_location = get_module_save_file_location(powertoy_key);
json::to_file(save_file_location, settings);
}
json::JsonObject load_module_settings(std::wstring_view powertoy_name)
json::JsonObject load_module_settings(std::wstring_view powertoy_key)
{
const std::wstring save_file_location = get_module_save_file_location(powertoy_name);
const std::wstring save_file_location = get_module_save_file_location(powertoy_key);
auto saved_settings = json::from_file(save_file_location);
return saved_settings.has_value() ? std::move(*saved_settings) : json::JsonObject{};
}

View File

@@ -1,6 +1,7 @@
#include "pch.h"
#include "settings_objects.h"
#include "settings_helpers.h"
#include <winrt/base.h>
namespace PowerToysSettings
{
@@ -277,27 +278,33 @@ namespace PowerToysSettings
return L"RESOURCE ID NOT FOUND: " + std::to_wstring(resource_id);
}
PowerToyValues::PowerToyValues(std::wstring_view powertoy_name)
PowerToyValues::PowerToyValues(std::wstring_view powertoy_name, std::wstring_view powertoy_key)
{
_name = powertoy_name;
_key = powertoy_key;
set_version();
m_json.SetNamedValue(L"name", json::value(powertoy_name));
m_json.SetNamedValue(L"properties", json::JsonObject{});
}
PowerToyValues PowerToyValues::from_json_string(std::wstring_view json)
PowerToyValues PowerToyValues::from_json_string(std::wstring_view json, std::wstring_view powertoy_key)
{
PowerToyValues result = PowerToyValues();
json::JsonObject jsonObject = json::JsonValue::Parse(json).GetObjectW();
if (!jsonObject.HasKey(L"name"))
{
throw winrt::hresult_error(E_NOT_SET, L"name field not set");
}
result.m_json = json::JsonValue::Parse(json).GetObjectW();
result._name = result.m_json.GetNamedString(L"name");
result._key = powertoy_key;
return result;
}
PowerToyValues PowerToyValues::load_from_settings_file(std::wstring_view powertoy_name)
PowerToyValues PowerToyValues::load_from_settings_file(std::wstring_view powertoy_key)
{
PowerToyValues result = PowerToyValues();
result.m_json = PTSettingsHelper::load_module_settings(powertoy_name);
result._name = powertoy_name;
result.m_json = PTSettingsHelper::load_module_settings(powertoy_key);
result._key = powertoy_key;
return result;
}
@@ -357,7 +364,7 @@ namespace PowerToysSettings
void PowerToyValues::save_to_settings_file()
{
set_version();
PTSettingsHelper::save_module_settings(_name, m_json);
PTSettingsHelper::save_module_settings(_key, m_json);
}
void PowerToyValues::set_version()

View File

@@ -67,9 +67,9 @@ namespace PowerToysSettings
class PowerToyValues
{
public:
PowerToyValues(std::wstring_view powertoy_name);
static PowerToyValues from_json_string(std::wstring_view json);
static PowerToyValues load_from_settings_file(std::wstring_view powertoy_name);
PowerToyValues(std::wstring_view powertoy_name, std::wstring_view powertoy_key);
static PowerToyValues from_json_string(std::wstring_view json, std::wstring_view powertoy_key);
static PowerToyValues load_from_settings_file(std::wstring_view powertoy_key);
template<typename T>
inline void add_property(std::wstring_view name, T value)
@@ -92,7 +92,7 @@ namespace PowerToysSettings
const std::wstring m_version = L"1.0";
void set_version();
json::JsonObject m_json;
std::wstring _name;
std::wstring _key;
PowerToyValues() {}
};

View File

@@ -0,0 +1,62 @@
#include "pch.h"
#include "toast_dont_show_again.h"
namespace notifications
{
bool disable_toast(const wchar_t* registry_path)
{
HKEY key{};
if (RegCreateKeyExW(HKEY_CURRENT_USER,
registry_path,
0,
nullptr,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
nullptr,
&key,
nullptr) != ERROR_SUCCESS)
{
return false;
}
const auto now = timeutil::now();
const size_t buf_size = sizeof(now);
if (RegSetValueExW(key, nullptr, 0, REG_QWORD, reinterpret_cast<const BYTE*>(&now), sizeof(now)) != ERROR_SUCCESS)
{
RegCloseKey(key);
return false;
}
RegCloseKey(key);
return true;
}
bool is_toast_disabled(const wchar_t* registry_path, const int64_t disable_interval_in_days)
{
HKEY key{};
if (RegOpenKeyExW(HKEY_CURRENT_USER,
registry_path,
0,
KEY_READ,
&key) != ERROR_SUCCESS)
{
return false;
}
std::wstring buffer(std::numeric_limits<time_t>::digits10 + 2, L'\0');
time_t last_disabled_time{};
DWORD time_size = static_cast<DWORD>(sizeof(last_disabled_time));
if (RegGetValueW(
key,
nullptr,
nullptr,
RRF_RT_REG_QWORD,
nullptr,
&last_disabled_time,
&time_size) != ERROR_SUCCESS)
{
RegCloseKey(key);
return false;
}
RegCloseKey(key);
return timeutil::diff::in_days(timeutil::now(), last_disabled_time) < disable_interval_in_days;
return false;
}
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include "timeutil.h"
namespace notifications
{
const inline wchar_t CantDragElevatedDontShowAgainRegistryPath[] = LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain\{e16ea82f-6d94-4f30-bb02-d6d911588afd})";
const inline int64_t CantDragElevatedDisableIntervalInDays = 30;
const inline wchar_t PreviewModulesDontShowAgainRegistryPath[] = LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain\{7e29e2b2-b31c-4dcd-b7b0-79c078b02430})";
const inline int64_t PreviewModulesDisableIntervalInDays = 30;
bool disable_toast(const wchar_t* registry_path);
bool is_toast_disabled(const wchar_t* registry_path, const int64_t disable_interval_in_days);
}

View File

@@ -1,150 +1,161 @@
#include "pch.h"
#include "toast_notifications_helper.h"
#include <common/notifications.h>
#include "updating.h"
#include "VersionHelper.h"
#include "version.h"
namespace
{
const wchar_t UPDATE_NOTIFY_TOAST_TAG[] = L"PTUpdateNotifyTag";
const wchar_t UPDATE_READY_TOAST_TAG[] = L"PTUpdateReadyTag";
const wchar_t TOAST_TITLE[] = L"PowerToys Update";
}
namespace localized_strings
{
const wchar_t GITHUB_NEW_VERSION_AVAILABLE[] = L"An update to PowerToys is available.\n";
const wchar_t GITHUB_NEW_VERSION_DOWNLOAD_STARTED[] = L"PowerToys download started.\n";
const wchar_t GITHUB_NEW_VERSION_READY_TO_INSTALL[] = L"An update to PowerToys is ready to install.\n";
const wchar_t GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR[] = L"Error: couldn't download PowerToys installer. Visit our GitHub page to update.\n";
const wchar_t GITHUB_NEW_VERSION_UPDATE_NOW[] = L"Update now";
const wchar_t GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART[] = L"At next launch";
const wchar_t UNINSTALLATION_UNKNOWN_ERROR[] = L"Error: please uninstall the previous version of PowerToys manually.";
const wchar_t GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT[] = L"An update to PowerToys is available. Visit our GitHub page to update.\n";
const wchar_t GITHUB_NEW_VERSION_UNAVAILABLE[] = L"PowerToys is up to date.\n";
const wchar_t GITHUB_NEW_VERSION_VISIT[] = L"Visit";
const wchar_t GITHUB_NEW_VERSION_MORE_INFO[] = L"More info...";
const wchar_t GITHUB_NEW_VERSION_ABORT[] = L"Abort";
const wchar_t GITHUB_NEW_VERSION_SNOOZE_TITLE[] = L"Click Snooze to be reminded in:";
const wchar_t GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D[] = L"1 day";
const wchar_t GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D[] = L"5 days";
const wchar_t DOWNLOAD_IN_PROGRESS[] = L"Downloading...";
const wchar_t DOWNLOAD_COMPLETE[] = L"Download complete";
}
namespace updating
{
namespace notifications
{
using namespace localized_strings;
std::wstring current_version_to_next_version(const updating::new_version_download_info& info)
{
auto current_version_to_next_version = VersionHelper{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION }.toWstring();
current_version_to_next_version += L" -> ";
current_version_to_next_version += info.version_string;
return current_version_to_next_version;
}
void show_unavailable()
{
::notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false };
std::wstring contents = GITHUB_NEW_VERSION_UNAVAILABLE;
::notifications::show_toast(std::move(contents), TOAST_TITLE, std::move(toast_params));
}
void show_available(const updating::new_version_download_info& info)
{
::notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false };
std::wstring contents = GITHUB_NEW_VERSION_AVAILABLE;
contents += current_version_to_next_version(info);
::notifications::show_toast_with_activations(std::move(contents),
TOAST_TITLE,
{},
{ ::notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://download_and_install_update/" }, ::notifications::link_button{ GITHUB_NEW_VERSION_MORE_INFO, info.release_page_uri.ToString().c_str() } },
std::move(toast_params));
}
void show_download_start(const updating::new_version_download_info& info)
{
::notifications::progress_bar_params progress_bar_params;
std::wstring progress_title{ info.version_string };
progress_title += L' ';
progress_title += localized_strings::DOWNLOAD_IN_PROGRESS;
progress_bar_params.progress_title = progress_title;
progress_bar_params.progress = .0f;
::notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false, std::move(progress_bar_params) };
::notifications::show_toast_with_activations(localized_strings::GITHUB_NEW_VERSION_DOWNLOAD_STARTED,
TOAST_TITLE,
{},
{},
std::move(toast_params));
}
void show_visit_github(const updating::new_version_download_info& info)
{
::notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false };
std::wstring contents = GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT;
contents += current_version_to_next_version(info);
::notifications::show_toast_with_activations(std::move(contents),
TOAST_TITLE,
{},
{ ::notifications::link_button{ GITHUB_NEW_VERSION_VISIT, info.release_page_uri.ToString().c_str() } },
std::move(toast_params));
}
void show_install_error(const updating::new_version_download_info& info)
{
::notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false };
std::wstring contents = GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR;
contents += current_version_to_next_version(info);
::notifications::show_toast_with_activations(std::move(contents),
TOAST_TITLE,
{},
{ ::notifications::link_button{ GITHUB_NEW_VERSION_VISIT, info.release_page_uri.ToString().c_str() } },
std::move(toast_params));
}
void show_version_ready(const updating::new_version_download_info& info)
{
::notifications::toast_params toast_params{ UPDATE_READY_TOAST_TAG, false };
std::wstring new_version_ready{ GITHUB_NEW_VERSION_READY_TO_INSTALL };
new_version_ready += current_version_to_next_version(info);
::notifications::show_toast_with_activations(std::move(new_version_ready),
TOAST_TITLE,
{},
{ ::notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://update_now/" + info.installer_filename },
::notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART, L"powertoys://schedule_update/" + info.installer_filename },
::notifications::snooze_button{ GITHUB_NEW_VERSION_SNOOZE_TITLE, { { GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D, 24 * 60 }, { GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D, 120 * 60 } } } },
std::move(toast_params));
}
void show_uninstallation_error()
{
::notifications::show_toast(localized_strings::UNINSTALLATION_UNKNOWN_ERROR, TOAST_TITLE);
}
void update_download_progress(const updating::new_version_download_info& info, float progress)
{
::notifications::progress_bar_params progress_bar_params;
std::wstring progress_title{ info.version_string };
progress_title += L' ';
progress_title += progress < 1 ? localized_strings::DOWNLOAD_IN_PROGRESS : localized_strings::DOWNLOAD_COMPLETE;
progress_bar_params.progress_title = progress_title;
progress_bar_params.progress = progress;
::notifications::update_progress_bar_toast(UPDATE_NOTIFY_TOAST_TAG, progress_bar_params);
}
}
#include "pch.h"
#include "notifications.h"
#include <common/notifications.h>
#include "updating.h"
#include "VersionHelper.h"
#include "version.h"
namespace
{
const wchar_t TOAST_TITLE[] = L"PowerToys Update";
}
namespace localized_strings
{
const wchar_t GITHUB_NEW_VERSION_AVAILABLE[] = L"An update to PowerToys is available.\n";
const wchar_t GITHUB_NEW_VERSION_DOWNLOAD_STARTED[] = L"PowerToys download started.\n";
const wchar_t GITHUB_NEW_VERSION_READY_TO_INSTALL[] = L"An update to PowerToys is ready to install.\n";
const wchar_t GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR[] = L"Error: couldn't download PowerToys installer. Visit our GitHub page to update.\n";
const wchar_t GITHUB_NEW_VERSION_UPDATE_NOW[] = L"Update now";
const wchar_t GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART[] = L"At next launch";
const wchar_t UNINSTALLATION_UNKNOWN_ERROR[] = L"Error: please uninstall the previous version of PowerToys manually.";
const wchar_t GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT[] = L"An update to PowerToys is available. Visit our GitHub page to update.\n";
const wchar_t GITHUB_NEW_VERSION_UNAVAILABLE[] = L"PowerToys is up to date.\n";
const wchar_t GITHUB_NEW_VERSION_VISIT[] = L"Visit";
const wchar_t GITHUB_NEW_VERSION_MORE_INFO[] = L"More info...";
const wchar_t GITHUB_NEW_VERSION_ABORT[] = L"Abort";
const wchar_t GITHUB_NEW_VERSION_SNOOZE_TITLE[] = L"Click Snooze to be reminded in:";
const wchar_t GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D[] = L"1 day";
const wchar_t GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D[] = L"5 days";
const wchar_t DOWNLOAD_IN_PROGRESS[] = L"Downloading...";
const wchar_t DOWNLOAD_COMPLETE[] = L"Download complete";
}
namespace updating
{
namespace notifications
{
using namespace localized_strings;
using namespace ::notifications;
std::wstring current_version_to_next_version(const updating::new_version_download_info& info)
{
auto current_version_to_next_version = VersionHelper{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION }.toWstring();
current_version_to_next_version += L" -> ";
current_version_to_next_version += info.version_string;
return current_version_to_next_version;
}
void show_unavailable()
{
remove_toasts(UPDATING_PROCESS_TOAST_TAG);
toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false };
std::wstring contents = GITHUB_NEW_VERSION_UNAVAILABLE;
show_toast(std::move(contents), TOAST_TITLE, std::move(toast_params));
}
void show_available(const updating::new_version_download_info& info)
{
remove_toasts(UPDATING_PROCESS_TOAST_TAG);
toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false };
std::wstring contents = GITHUB_NEW_VERSION_AVAILABLE;
contents += current_version_to_next_version(info);
show_toast_with_activations(std::move(contents),
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() } },
std::move(toast_params));
}
void show_download_start(const updating::new_version_download_info& info)
{
remove_toasts(UPDATING_PROCESS_TOAST_TAG);
progress_bar_params progress_bar_params;
std::wstring progress_title{ info.version_string };
progress_title += L' ';
progress_title += localized_strings::DOWNLOAD_IN_PROGRESS;
progress_bar_params.progress_title = progress_title;
progress_bar_params.progress = .0f;
toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false, std::move(progress_bar_params) };
show_toast_with_activations(localized_strings::GITHUB_NEW_VERSION_DOWNLOAD_STARTED,
TOAST_TITLE,
{},
{},
std::move(toast_params));
}
void show_visit_github(const updating::new_version_download_info& info)
{
remove_toasts(UPDATING_PROCESS_TOAST_TAG);
toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false };
std::wstring contents = GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT;
contents += current_version_to_next_version(info);
show_toast_with_activations(std::move(contents),
TOAST_TITLE,
{},
{ link_button{ GITHUB_NEW_VERSION_VISIT, info.release_page_uri.ToString().c_str() } },
std::move(toast_params));
}
void show_install_error(const updating::new_version_download_info& info)
{
remove_toasts(UPDATING_PROCESS_TOAST_TAG);
toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false };
std::wstring contents = GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR;
contents += current_version_to_next_version(info);
show_toast_with_activations(std::move(contents),
TOAST_TITLE,
{},
{ link_button{ GITHUB_NEW_VERSION_VISIT, info.release_page_uri.ToString().c_str() } },
std::move(toast_params));
}
void show_version_ready(const updating::new_version_download_info& info)
{
remove_toasts(UPDATING_PROCESS_TOAST_TAG);
toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false };
std::wstring new_version_ready{ GITHUB_NEW_VERSION_READY_TO_INSTALL };
new_version_ready += current_version_to_next_version(info);
show_toast_with_activations(std::move(new_version_ready),
TOAST_TITLE,
{},
{ link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://update_now/" + info.installer_filename },
link_button{ GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART, L"powertoys://schedule_update/" + info.installer_filename },
snooze_button{ GITHUB_NEW_VERSION_SNOOZE_TITLE, { { GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D, 24 * 60 }, { GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D, 120 * 60 } } } },
std::move(toast_params));
}
void show_uninstallation_error()
{
remove_toasts(UPDATING_PROCESS_TOAST_TAG);
show_toast(localized_strings::UNINSTALLATION_UNKNOWN_ERROR, TOAST_TITLE);
}
void update_download_progress(const updating::new_version_download_info& info, float progress)
{
progress_bar_params progress_bar_params;
std::wstring progress_title{ info.version_string };
progress_title += L' ';
progress_title += progress < 1 ? localized_strings::DOWNLOAD_IN_PROGRESS : localized_strings::DOWNLOAD_COMPLETE;
progress_bar_params.progress_title = progress_title;
progress_bar_params.progress = progress;
update_toast_progress_bar(UPDATING_PROCESS_TOAST_TAG, progress_bar_params);
}
}
}

View File

@@ -1,19 +1,19 @@
#pragma once
namespace updating
{
struct new_version_download_info;
namespace notifications
{
void show_unavailable();
void show_available(const updating::new_version_download_info& info);
void show_download_start(const updating::new_version_download_info& info);
void show_visit_github(const updating::new_version_download_info& info);
void show_install_error(const updating::new_version_download_info& info);
void show_version_ready(const updating::new_version_download_info& info);
void show_uninstallation_error();
void update_download_progress(const updating::new_version_download_info& info, float progress);
}
#pragma once
namespace updating
{
struct new_version_download_info;
namespace notifications
{
void show_unavailable();
void show_available(const updating::new_version_download_info& info);
void show_download_start(const updating::new_version_download_info& info);
void show_visit_github(const updating::new_version_download_info& info);
void show_install_error(const updating::new_version_download_info& info);
void show_version_ready(const updating::new_version_download_info& info);
void show_uninstallation_error();
void update_download_progress(const updating::new_version_download_info& info, float progress);
}
}

View File

@@ -3,8 +3,8 @@
#include "version.h"
#include "http_client.h"
#include "notifications.h"
#include "updating.h"
#include "toast_notifications_helper.h"
#include <msi.h>
#include <common/common.h>
@@ -102,6 +102,11 @@ namespace updating
std::future<std::optional<new_version_download_info>> get_new_github_version_info_async()
{
// If the current version starts with 0.0.*, it means we're on a local build from a farm and shouldn't check for updates.
if (VERSION_MAJOR == 0 && VERSION_MINOR == 0)
{
co_return std::nullopt;
}
try
{
http::HttpClient client;

View File

@@ -175,14 +175,14 @@
<ItemGroup>
<ClInclude Include="dotnet_installation.h" />
<ClInclude Include="http_client.h" />
<ClInclude Include="toast_notifications_helper.h" />
<ClInclude Include="notifications.h" />
<ClInclude Include="updating.h" />
<ClInclude Include="pch.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dotnet_installation.cpp" />
<ClCompile Include="http_client.cpp" />
<ClCompile Include="toast_notifications_helper.cpp" />
<ClCompile Include="notifications.cpp" />
<ClCompile Include="updating.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>

View File

@@ -21,15 +21,15 @@
<ClInclude Include="updating.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="toast_notifications_helper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="http_client.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="dotnet_installation.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="notifications.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
@@ -38,15 +38,15 @@
<ClCompile Include="updating.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="toast_notifications_helper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="http_client.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dotnet_installation.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="notifications.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -2,10 +2,11 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class AppSpecificKeysDataModel : KeysDataModel
{
@@ -24,7 +25,15 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
public bool Compare(AppSpecificKeysDataModel arg)
{
return OriginalKeys.Equals(arg.OriginalKeys) && NewRemapKeys.Equals(arg.NewRemapKeys) && TargetApp.Equals(arg.TargetApp);
if (arg == null)
{
throw new ArgumentNullException(nameof(arg));
}
// Using Ordinal comparison for internal text
return OriginalKeys.Equals(arg.OriginalKeys, StringComparison.Ordinal) &&
NewRemapKeys.Equals(arg.NewRemapKeys, StringComparison.Ordinal) &&
TargetApp.Equals(arg.TargetApp, StringComparison.Ordinal);
}
}
}

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public abstract class BasePTModuleSettings
{

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class BoolProperty
{

View File

@@ -6,7 +6,7 @@ using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class BoolPropertyJsonConverter : JsonConverter<bool>
{

View File

@@ -5,12 +5,34 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public enum ColorRepresentationType
{
/// <summary>
/// Color presentation as hexadecimal color value without the alpha-value (e.g. #0055FF)
/// </summary>
HEX = 0,
/// <summary>
/// Color presentation as RGB color value (red[0..255], green[0..255], blue[0..255])
/// </summary>
RGB = 1,
/// <summary>
/// Color presentation as CMYK color value (cyan[0%..100%], magenta[0%..100%], yellow[0%..100%], black key[0%..100%])
/// </summary>
CMYK = 2,
/// <summary>
/// Color presentation as HSL color value (hue[0°..360°], saturation[0..100%], lightness[0%..100%])
/// </summary>
HSL = 3,
/// <summary>
/// Color presentation as HSV color value (hue[0°..360°], saturation[0%..100%], value[0%..100%])
/// </summary>
HSV = 4,
}
public class ColorPickerProperties
@@ -31,8 +53,6 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
public ColorRepresentationType CopiedColorRepresentation { get; set; }
public override string ToString()
{
return JsonSerializer.Serialize(this);
}
=> JsonSerializer.Serialize(this);
}
}

View File

@@ -2,11 +2,12 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class ColorPickerSettings : BasePTModuleSettings, ISettingsConfig
{
@@ -30,6 +31,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
WriteIndented = true,
};
if (settingsUtils == null)
{
throw new ArgumentNullException(nameof(settingsUtils));
}
settingsUtils.SaveSettings(JsonSerializer.Serialize(this, options), ModuleName);
}

View File

@@ -2,7 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public static class ConfigDefaults
{

View File

@@ -4,7 +4,7 @@
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib.CustomAction
namespace Microsoft.PowerToys.Settings.UI.Library.CustomAction
{
public class CustomActionDataModel
{

View File

@@ -5,7 +5,7 @@
using System;
using System.Text.Json;
namespace Microsoft.PowerToys.Settings.UI.Lib.CustomAction
namespace Microsoft.PowerToys.Settings.UI.Library.CustomAction
{
public class CustomNamePolicy : JsonNamingPolicy
{

View File

@@ -2,7 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.PowerToys.Settings.UI.Lib.CustomAction
namespace Microsoft.PowerToys.Settings.UI.Library.CustomAction
{
public class ModuleCustomAction
{

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib.CustomAction
namespace Microsoft.PowerToys.Settings.UI.Library.CustomAction
{
public class SendCustomAction
{
@@ -25,7 +25,8 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.CustomAction
{
PropertyNamingPolicy = new CustomNamePolicy((propertyName) =>
{
return propertyName.Equals("ModuleAction") ? moduleName : propertyName;
// Using Ordinal as this is an internal property name
return propertyName.Equals("ModuleAction", System.StringComparison.Ordinal) ? moduleName : propertyName;
}),
};

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
// Represents the configuration property of the settings that store Double type.
public class DoubleProperty

View File

@@ -8,7 +8,7 @@ using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.Telemetry;
using Microsoft.PowerToys.Telemetry;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class EnabledModules
{

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class FZConfigProperties
{

View File

@@ -3,9 +3,9 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class FancyZonesSettings : BasePTModuleSettings, ISettingsConfig
{

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class FancyZonesSettingsIPCMessage
{

View File

@@ -3,12 +3,13 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Lib.Utilities;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class GeneralSettings : ISettingsConfig
{
@@ -53,6 +54,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
[JsonPropertyName("download_updates_automatically")]
public bool AutoDownloadUpdates { get; set; }
[SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Any error from calling interop code should not prevent the program from loading.")]
public GeneralSettings()
{
Packaged = false;
@@ -66,8 +68,9 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
{
PowertoysVersion = DefaultPowertoysVersion();
}
catch
catch (Exception e)
{
Logger.LogError("Exception encountered when getting PowerToys version", e);
PowertoysVersion = "v0.0.0";
}

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class GeneralSettingsCustomAction
{

View File

@@ -4,7 +4,7 @@
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class GenericProperty<T>
{

View File

@@ -0,0 +1,27 @@
// 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.ComponentModel;
using System.Runtime.CompilerServices;
namespace Microsoft.PowerToys.Settings.UI.Library.Helpers
{
public class Observable : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
{
return;
}
storage = value;
OnPropertyChanged(propertyName);
}
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

View File

@@ -4,9 +4,9 @@
using System.Text;
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Utilities;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class HotkeySettings
{

View File

@@ -3,9 +3,10 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics.CodeAnalysis;
using interop;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public delegate void KeyEvent(int key);
@@ -13,17 +14,19 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
public delegate bool FilterAccessibleKeyboardEvents(int key, UIntPtr extraInfo);
public class HotkeySettingsControlHook
public class HotkeySettingsControlHook : IDisposable
{
private const int WmKeyDown = 0x100;
private const int WmKeyUp = 0x101;
private const int WmSysKeyDown = 0x0104;
private const int WmSysKeyUp = 0x0105;
[SuppressMessage("Usage", "CA2213:Disposable fields should be disposed", Justification = "This class conforms to the IDisposable pattern, and the Dispose and C++ destructor does get called when debugging. Looks like a false positive from FxCop.")]
private KeyboardHook _hook;
private KeyEvent _keyDown;
private KeyEvent _keyUp;
private IsActive _isActive;
private bool disposedValue;
private FilterAccessibleKeyboardEvents _filterKeyboardEvent;
@@ -62,10 +65,24 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
return _filterKeyboardEvent(ev.key, (UIntPtr)ev.dwExtraInfo);
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// Dispose the KeyboardHook object to terminate the hook threads
_hook.Dispose();
}
disposedValue = true;
}
}
public void Dispose()
{
// Dispose the KeyboardHook object to terminate the hook threads
_hook.Dispose();
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -2,9 +2,9 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public interface ISettingsUtils
{

View File

@@ -6,7 +6,7 @@ using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class ImageResizerProperties
{

View File

@@ -4,9 +4,9 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class ImageResizerSettings : BasePTModuleSettings, ISettingsConfig
{

View File

@@ -2,12 +2,13 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class ImageSize : INotifyPropertyChanged
{
@@ -222,6 +223,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
public void Update(ImageSize modifiedSize)
{
if (modifiedSize == null)
{
throw new ArgumentNullException(nameof(modifiedSize));
}
Id = modifiedSize.Id;
Name = modifiedSize.Name;
Fit = modifiedSize.Fit;

View File

@@ -4,7 +4,7 @@
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class ImagerResizerKeepDateModified
{

View File

@@ -4,7 +4,7 @@
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class ImageResizerCustomSizeProperty
{

View File

@@ -4,7 +4,7 @@
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class ImageResizerFallbackEncoder
{

View File

@@ -6,12 +6,17 @@ using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class ImageResizerSizes
{
// Suppressing this warning because removing the setter breaks
// deserialization with System.Text.Json. This affects the UI display.
// See: https://github.com/dotnet/runtime/issues/30258
[JsonPropertyName("value")]
#pragma warning disable CA2227 // Collection properties should be read only
public ObservableCollection<ImageSize> Value { get; set; }
#pragma warning restore CA2227 // Collection properties should be read only
public ImageResizerSizes()
{

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
// Represents the configuration property of the settings that store Integer type.
public class IntProperty

View File

@@ -2,7 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.PowerToys.Settings.UI.Lib.Interface
namespace Microsoft.PowerToys.Settings.UI.Library.Interfaces
{
// Common interface to be implemented by all the objects which get and store settings properties.
public interface ISettingsConfig

View File

@@ -2,7 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.PowerToys.Settings.UI.Lib.Interface
namespace Microsoft.PowerToys.Settings.UI.Library.Interfaces
{
public interface ISettingsRepository<T>
{

View File

@@ -4,7 +4,7 @@
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class KeyboardKeysProperty
{

View File

@@ -4,9 +4,9 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class KeyboardManagerProfile : ISettingsConfig
{

View File

@@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class KeyboardManagerProperties
{

View File

@@ -3,9 +3,9 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class KeyboardManagerSettings : BasePTModuleSettings, ISettingsConfig
{

View File

@@ -6,9 +6,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Utilities;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class KeysDataModel
{

View File

@@ -8,13 +8,14 @@
<Version>$(Version).0</Version>
<Authors>Microsoft Corporation</Authors>
<Product>PowerToys</Product>
<Description>PowerToys Settings UI Lib</Description>
<Description>PowerToys Settings UI Library</Description>
<Copyright>Copyright (C) 2020 Microsoft Corporation</Copyright>
<RepositoryUrl>https://github.com/microsoft/PowerToys</RepositoryUrl>
<RepositoryType>Github</RepositoryType>
<PackageTags>PowerToys</PackageTags>
<NeutralLanguage>en-US</NeutralLanguage>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AssemblyName>Microsoft.PowerToys.Settings.UI.Lib</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -41,6 +42,11 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Text.Json" Version="4.7.2" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers">
<Version>3.3.0</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class OutGoingGeneralSettings
{

View File

@@ -4,7 +4,7 @@
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class PowerLauncherProperties
{

View File

@@ -2,11 +2,12 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class PowerLauncherSettings : BasePTModuleSettings, ISettingsConfig
{
@@ -30,6 +31,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
WriteIndented = true,
};
if (settingsUtils == null)
{
throw new ArgumentNullException(nameof(settingsUtils));
}
settingsUtils.SaveSettings(JsonSerializer.Serialize(this, options), ModuleName);
}

View File

@@ -8,7 +8,7 @@ using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.Telemetry;
using Microsoft.PowerToys.Telemetry;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class PowerPreviewProperties
{

View File

@@ -3,9 +3,9 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class PowerPreviewSettings : BasePTModuleSettings, ISettingsConfig
{

View File

@@ -3,9 +3,9 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class PowerRenameLocalProperties : ISettingsConfig
{

View File

@@ -4,7 +4,7 @@
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class PowerRenameProperties
{

View File

@@ -2,10 +2,11 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class PowerRenameSettings : BasePTModuleSettings, ISettingsConfig
{
@@ -23,6 +24,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
public PowerRenameSettings(PowerRenameLocalProperties localProperties)
{
if (localProperties == null)
{
throw new ArgumentNullException(nameof(localProperties));
}
Properties = new PowerRenameProperties();
Properties.PersistState.Value = localProperties.PersistState;
Properties.MRUEnabled.Value = localProperties.MRUEnabled;

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class PowerRenameSettingsIPCMessage
{

View File

@@ -6,12 +6,17 @@ using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class RemapKeysDataModel
{
// Suppressing this warning because removing the setter breaks
// deserialization with System.Text.Json. This affects the UI display.
// See: https://github.com/dotnet/runtime/issues/30258
[JsonPropertyName("inProcess")]
#pragma warning disable CA2227 // Collection properties should be read only
public List<KeysDataModel> InProcessRemapKeys { get; set; }
#pragma warning restore CA2227 // Collection properties should be read only
public RemapKeysDataModel()
{

View File

@@ -3,9 +3,9 @@
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
// This Singleton class is a wrapper around the settings configurations that are accessed by viewmodels.
// This class can have only one instance and therefore the settings configurations are common to all.
@@ -20,7 +20,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
private T settingsConfig;
// Suppressing the warning as this is a singleton class and this method is
// necessarily static
#pragma warning disable CA1000 // Do not declare static members on generic types
public static SettingsRepository<T> GetInstance(ISettingsUtils settingsUtils)
#pragma warning restore CA1000 // Do not declare static members on generic types
{
// To ensure that only one instance of Settings Repository is created in a multi-threaded environment.
lock (_SettingsRepoLock)

View File

@@ -3,11 +3,13 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text.Json;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Lib.Utilities;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class SettingsUtils : ISettingsUtils
{
@@ -100,6 +102,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
}
// Save settings to a json file.
[SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "General exceptions will be logged until we can better understand runtime exception scenarios")]
public void SaveSettings(string jsonSettings, string powertoy = DefaultModuleName, string fileName = DefaultFileName)
{
try
@@ -114,8 +117,15 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
_ioProvider.WriteAllText(GetSettingsPath(powertoy, fileName), jsonSettings);
}
}
catch
catch (Exception e)
{
Logger.LogError($"Exception encountered while saving {powertoy} settings.", e);
#if DEBUG
if (e is ArgumentException || e is ArgumentNullException || e is PathTooLongException)
{
throw;
}
#endif
}
}

View File

@@ -4,7 +4,7 @@
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class ShortcutGuideProperties
{

View File

@@ -3,9 +3,9 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class ShortcutGuideSettings : BasePTModuleSettings, ISettingsConfig
{

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class ShortcutGuideSettingsIPCMessage
{

View File

@@ -6,15 +6,22 @@ using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class ShortcutsKeyDataModel
{
// Suppressing these warnings because removing the setter breaks
// deserialization with System.Text.Json. This affects the UI display.
// See: https://github.com/dotnet/runtime/issues/30258
[JsonPropertyName("global")]
#pragma warning disable CA2227 // Collection properties should be read only
public List<KeysDataModel> GlobalRemapShortcuts { get; set; }
#pragma warning restore CA2227 // Collection properties should be read only
[JsonPropertyName("appSpecific")]
#pragma warning disable CA2227 // Collection properties should be read only
public List<AppSpecificKeysDataModel> AppSpecificRemapShortcuts { get; set; }
#pragma warning restore CA2227 // Collection properties should be read only
public ShortcutsKeyDataModel()
{

View File

@@ -4,7 +4,7 @@
using System.Text.Json;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class SndFancyZonesSettings
{

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class SndImageResizerSettings
{

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class SndKeyboardManagerSettings
{

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
// Represents a powertoys module settings setnt to the runner.
public class SndModuleSettings<T>

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class SndPowerPreviewSettings
{

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class SndPowerRenameSettings
{

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class SndShortcutGuideSettings
{

View File

@@ -5,7 +5,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
namespace Microsoft.PowerToys.Settings.UI.Library
{
// Represents the configuration property of the settings that store string type.
public class StringProperty

View File

@@ -7,9 +7,9 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.PowerToys.Settings.UI.Lib.CustomAction;
using Microsoft.PowerToys.Settings.UI.Library.CustomAction;
namespace Microsoft.PowerToys.Settings.UI.Lib.Utilities
namespace Microsoft.PowerToys.Settings.UI.Library.Utilities
{
public static class Helper
{
@@ -92,6 +92,15 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.Utilities
{
// Split up the version strings into int[]
// Example: v10.0.2 -> {10, 0, 2};
if (version1 == null)
{
throw new ArgumentNullException(nameof(version1));
}
else if (version2 == null)
{
throw new ArgumentNullException(nameof(version2));
}
var v1 = version1.Substring(1).Split('.').Select(int.Parse).ToArray();
var v2 = version2.Substring(1).Split('.').Select(int.Parse).ToArray();

View File

@@ -2,7 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.PowerToys.Settings.UI.Lib.Utilities
namespace Microsoft.PowerToys.Settings.UI.Library.Utilities
{
public interface IIOProvider
{

View File

@@ -0,0 +1,65 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
namespace Microsoft.PowerToys.Settings.UI.Library.Utilities
{
public static class Logger
{
private static readonly string ApplicationLogPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\PowerToys\\Settings Logs");
static Logger()
{
if (!Directory.Exists(ApplicationLogPath))
{
Directory.CreateDirectory(ApplicationLogPath);
}
var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
Trace.AutoFlush = true;
}
public static void LogInfo(string message)
{
Log(message, "INFO");
}
public static void LogError(string message, Exception e)
{
Log(
message + Environment.NewLine +
e?.Message + Environment.NewLine +
"Inner exception: " + Environment.NewLine +
e?.InnerException?.Message + Environment.NewLine +
"Stack trace: " + Environment.NewLine +
e?.StackTrace,
"ERROR");
}
private static void Log(string message, string type)
{
Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay);
Trace.Indent();
Trace.WriteLine(GetCallerInfo());
Trace.WriteLine(message);
Trace.Unindent();
}
private static string GetCallerInfo()
{
StackTrace stackTrace = new StackTrace();
var methodName = stackTrace.GetFrame(3)?.GetMethod();
var className = methodName?.DeclaringType.Name;
return "[Method]: " + methodName?.Name + " [Class]: " + className;
}
}
}

View File

@@ -7,7 +7,7 @@ using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace Microsoft.PowerToys.Settings.UI.Lib.Utilities
namespace Microsoft.PowerToys.Settings.UI.Library.Utilities
{
internal static class NativeMethods
{

View File

@@ -4,7 +4,7 @@
using System.IO;
namespace Microsoft.PowerToys.Settings.UI.Lib.Utilities
namespace Microsoft.PowerToys.Settings.UI.Library.Utilities
{
public class SystemIOProvider : IIOProvider
{

View File

@@ -3,11 +3,12 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Text.Json;
using Microsoft.PowerToys.Settings.UI.Lib.Helpers;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
{
public class ColorPickerViewModel : Observable
{
@@ -24,6 +25,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
public ColorPickerViewModel(ISettingsUtils settingsUtils, ISettingsRepository<GeneralSettings> settingsRepository, Func<string, int> ipcMSGCallBackFunc)
{
// Obtain the general PowerToy settings configurations
if (settingsRepository == null)
{
throw new ArgumentNullException(nameof(settingsRepository));
}
GeneralSettingsConfig = settingsRepository.SettingsConfig;
_settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils));
@@ -121,8 +127,13 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
private void NotifySettingsChanged()
{
// Using InvariantCulture as this is an IPC message
SendConfigMSG(
string.Format("{{ \"powertoys\": {{ \"{0}\": {1} }} }}", ColorPickerSettings.ModuleName, JsonSerializer.Serialize(_colorPickerSettings)));
string.Format(
CultureInfo.InvariantCulture,
"{{ \"powertoys\": {{ \"{0}\": {1} }} }}",
ColorPickerSettings.ModuleName,
JsonSerializer.Serialize(_colorPickerSettings)));
}
}
}

View File

@@ -5,7 +5,7 @@
using System;
using System.Windows.Input;
namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels.Commands
namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands
{
public class ButtonClickCommand : ICommand
{

View File

@@ -5,7 +5,7 @@
using System;
using System.Windows.Input;
namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels.Commands
namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands
{
public class RelayCommand : ICommand
{

View File

@@ -5,7 +5,7 @@
using System;
using System.Windows.Input;
namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels.Commands
namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands
{
public class RelayCommand<T> : ICommand
{

View File

@@ -4,12 +4,13 @@
using System;
using System.Drawing;
using System.Globalization;
using System.Runtime.CompilerServices;
using Microsoft.PowerToys.Settings.UI.Lib.Helpers;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Lib.ViewModels.Commands;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
{
public class FancyZonesViewModel : Observable
{
@@ -28,10 +29,20 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
public FancyZonesViewModel(ISettingsRepository<GeneralSettings> settingsRepository, ISettingsRepository<FancyZonesSettings> moduleSettingsRepository, Func<string, int> ipcMSGCallBackFunc, string configFileSubfolder = "")
{
// To obtain the general settings configurations of PowerToys Settings.
if (settingsRepository == null)
{
throw new ArgumentNullException(nameof(settingsRepository));
}
GeneralSettingsConfig = settingsRepository.SettingsConfig;
settingsConfigFileFolder = configFileSubfolder;
// To obtain the settings configurations of Fancy zones.
if (moduleSettingsRepository == null)
{
throw new ArgumentNullException(nameof(moduleSettingsRepository));
}
Settings = moduleSettingsRepository.SettingsConfig;
LaunchEditorEventHandler = new ButtonClickCommand(LaunchEditor);
@@ -137,7 +148,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_shiftDrag = value;
Settings.Properties.FancyzonesShiftDrag.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -155,7 +166,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_mouseSwitch = value;
Settings.Properties.FancyzonesMouseSwitch.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -178,7 +189,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_overrideSnapHotkeys = value;
Settings.Properties.FancyzonesOverrideSnapHotkeys.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
OnPropertyChanged(nameof(SnapHotkeysCategoryEnabled));
}
}
@@ -197,7 +208,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_moveWindowsAcrossMonitors = value;
Settings.Properties.FancyzonesMoveWindowsAcrossMonitors.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -215,7 +226,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_moveWindowsBasedOnPosition = value;
Settings.Properties.FancyzonesMoveWindowsBasedOnPosition.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -233,7 +244,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_displayChangemoveWindows = value;
Settings.Properties.FancyzonesDisplayChangeMoveWindows.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -251,7 +262,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_zoneSetChangeMoveWindows = value;
Settings.Properties.FancyzonesZoneSetChangeMoveWindows.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -269,7 +280,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_appLastZoneMoveWindows = value;
Settings.Properties.FancyzonesAppLastZoneMoveWindows.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -287,7 +298,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_openWindowOnActiveMonitor = value;
Settings.Properties.FancyzonesOpenWindowOnActiveMonitor.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -305,7 +316,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_restoreSize = value;
Settings.Properties.FancyzonesRestoreSize.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -323,7 +334,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_useCursorPosEditorStartupScreen = value;
Settings.Properties.UseCursorposEditorStartupscreen.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -341,7 +352,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_showOnAllMonitors = value;
Settings.Properties.FancyzonesShowOnAllMonitors.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -359,7 +370,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_spanZonesAcrossMonitors = value;
Settings.Properties.FancyzonesSpanZonesAcrossMonitors.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -377,11 +388,13 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_makeDraggedWindowTransparent = value;
Settings.Properties.FancyzonesMakeDraggedWindowTransparent.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
// For the following setters we use OrdinalIgnoreCase string comparison since
// we expect value to be a hex code.
public string ZoneHighlightColor
{
get
@@ -391,12 +404,15 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
set
{
value = ToRGBHex(value);
if (!value.Equals(_zoneHighlightColor))
// The fallback value is based on ToRGBHex's behavior, which returns
// #FFFFFF if any exceptions are encountered, e.g. from passing in a null value.
// This extra handling is added here to deal with FxCop warnings.
value = (value != null) ? ToRGBHex(value) : "#FFFFFF";
if (!value.Equals(_zoneHighlightColor, StringComparison.OrdinalIgnoreCase))
{
_zoneHighlightColor = value;
Settings.Properties.FancyzonesZoneHighlightColor.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -410,12 +426,15 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
set
{
value = ToRGBHex(value);
// The fallback value is based on ToRGBHex's behavior, which returns
// #FFFFFF if any exceptions are encountered, e.g. from passing in a null value.
// This extra handling is added here to deal with FxCop warnings.
value = (value != null) ? ToRGBHex(value) : "#FFFFFF";
if (!value.Equals(_zoneBorderColor, StringComparison.OrdinalIgnoreCase))
{
_zoneBorderColor = value;
Settings.Properties.FancyzonesBorderColor.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -429,12 +448,15 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
set
{
value = ToRGBHex(value);
if (!value.Equals(_zoneInActiveColor))
// The fallback value is based on ToRGBHex's behavior, which returns
// #FFFFFF if any exceptions are encountered, e.g. from passing in a null value.
// This extra handling is added here to deal with FxCop warnings.
value = (value != null) ? ToRGBHex(value) : "#FFFFFF";
if (!value.Equals(_zoneInActiveColor, StringComparison.OrdinalIgnoreCase))
{
_zoneInActiveColor = value;
Settings.Properties.FancyzonesInActiveColor.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -452,7 +474,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_highlightOpacity = value;
Settings.Properties.FancyzonesHighlightOpacity.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -468,7 +490,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
if (value != _editorHotkey)
{
if (value.IsEmpty())
if (value == null || value.IsEmpty())
{
_editorHotkey = FZConfigProperties.DefaultHotkeyValue;
}
@@ -478,7 +500,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
}
Settings.Properties.FancyzonesEditorHotkey.Value = _editorHotkey;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -496,7 +518,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_excludedApps = value;
Settings.Properties.FancyzonesExcludedApps.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -507,7 +529,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
SendConfigMSG("{\"action\":{\"FancyZones\":{\"action_name\":\"ToggledFZEditor\", \"value\":\"\"}}}");
}
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(propertyName);
if (SendConfigMSG != null)
@@ -520,13 +542,21 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
private static string ToRGBHex(string color)
{
try
// Using InvariantCulture as these are expected to be hex codes.
bool success = int.TryParse(
color.Replace("#", string.Empty),
System.Globalization.NumberStyles.HexNumber,
CultureInfo.InvariantCulture,
out int argb);
if (success)
{
int argb = int.Parse(color.Replace("#", string.Empty), System.Globalization.NumberStyles.HexNumber);
Color clr = Color.FromArgb(argb);
return "#" + clr.R.ToString("X2") + clr.G.ToString("X2") + clr.B.ToString("X2");
return "#" + clr.R.ToString("X2", CultureInfo.InvariantCulture) +
clr.G.ToString("X2", CultureInfo.InvariantCulture) +
clr.B.ToString("X2", CultureInfo.InvariantCulture);
}
catch (Exception)
else
{
return "#FFFFFF";
}

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