[fxcop] Settings UI library (part 2) (#7257)

* Suppress warnings for read-only collection properties (see code comments)

* Call ConfigureAwait on tasks

* Add CultureInfo and StringComparison policy for certain string operations

* Add checks and exceptions for null arguments to public methods

* Rename RaisePropertyChanged to NotifyPropertyChanged

* Suppress CA1000 warning on SettingsRepository class

* Implement Disposable pattern in HotkeySettingsControlHook

* Modify null argument handling in KeyboardManagerViewModel::CombineShortcutLists
This commit is contained in:
Luthfi Mawarid
2020-10-19 13:32:05 -07:00
committed by GitHub
parent 03509e7f36
commit 688f134051
23 changed files with 326 additions and 65 deletions

View File

@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
@@ -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

@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
@@ -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

@@ -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

@@ -13,7 +13,7 @@ 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;
@@ -24,6 +24,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
private KeyEvent _keyDown;
private KeyEvent _keyUp;
private IsActive _isActive;
private bool disposedValue;
private FilterAccessibleKeyboardEvents _filterKeyboardEvent;
@@ -62,10 +63,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,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text.Json;
@@ -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

@@ -10,8 +10,13 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
{
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

@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
@@ -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,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
@@ -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

@@ -10,8 +10,13 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
{
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

@@ -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

@@ -10,11 +10,18 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
{
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

@@ -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

@@ -3,6 +3,7 @@
// 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;
@@ -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

@@ -4,6 +4,7 @@
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;
@@ -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)
@@ -522,9 +544,15 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
try
{
int argb = int.Parse(color.Replace("#", string.Empty), System.Globalization.NumberStyles.HexNumber);
// Using InvariantCulture as these are expected to be hex codes.
int argb = int.Parse(
color.Replace("#", string.Empty),
System.Globalization.NumberStyles.HexNumber,
CultureInfo.InvariantCulture);
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)
{

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Runtime.CompilerServices;
using Microsoft.PowerToys.Settings.UI.Lib.Helpers;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
@@ -39,6 +40,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
RestartElevatedButtonEventHandler = new ButtonClickCommand(RestartElevated);
// To obtain the general settings configuration of PowerToys if it exists, else to create a new file and return the default configurations.
if (settingsRepository == null)
{
throw new ArgumentNullException(nameof(settingsRepository));
}
GeneralSettingsConfig = settingsRepository.SettingsConfig;
// set the callback functions value to hangle outgoing IPC message.
@@ -48,20 +54,25 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
// set the callback function value to update the UI theme.
UpdateUIThemeCallBack = updateTheme;
UpdateUIThemeCallBack(GeneralSettingsConfig.Theme.ToLower());
UpdateUIThemeCallBack(GeneralSettingsConfig.Theme);
// Update Settings file folder:
_settingsConfigFileFolder = configFileSubfolder;
switch (GeneralSettingsConfig.Theme.ToLower())
// Using Invariant here as these are internal strings and fxcop
// expects strings to be normalized to uppercase. While the theme names
// are represented in lowercase everywhere else, we'll use uppercase
// normalization for switch statements
switch (GeneralSettingsConfig.Theme.ToUpperInvariant())
{
case "light":
case "LIGHT":
_isLightThemeRadioButtonChecked = true;
break;
case "dark":
case "DARK":
_isDarkThemeRadioButtonChecked = true;
break;
case "system":
case "SYSTEM":
_isSystemThemeRadioButtonChecked = true;
break;
}
@@ -102,7 +113,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
if (_packaged != value)
{
_packaged = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -121,7 +132,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_startup = value;
GeneralSettingsConfig.Startup = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -193,7 +204,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_runElevated = value;
GeneralSettingsConfig.RunElevated = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -220,7 +231,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_autoDownloadUpdates = value;
GeneralSettingsConfig.AutoDownloadUpdates = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -246,7 +257,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
}
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -272,7 +283,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
}
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -298,7 +309,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
}
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -329,12 +340,12 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
if (_latestAvailableVersion != value)
{
_latestAvailableVersion = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
// Notify UI of property change
OnPropertyChanged(propertyName);

View File

@@ -28,6 +28,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
_settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils));
// To obtain the general settings configurations of PowerToys.
if (settingsRepository == null)
{
throw new ArgumentNullException(nameof(settingsRepository));
}
GeneralSettingsConfig = settingsRepository.SettingsConfig;
try
@@ -92,13 +97,18 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
}
}
#pragma warning disable CA2227 // Collection properties should be read only
public ObservableCollection<ImageSize> Sizes
#pragma warning restore CA2227 // Collection properties should be read only
{
get
{
return _advancedSizes;
}
// FxCop demands collection properties to be read-only, but this
// setter is used in autogenerated files (ImageResizerPage.g.cs)
// and replacing the setter with its own method will break the file
set
{
SavesImageSizes(value);

View File

@@ -43,6 +43,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
public KeyboardManagerViewModel(ISettingsUtils settingsUtils, ISettingsRepository<GeneralSettings> settingsRepository, Func<string, int> ipcMSGCallBackFunc, Func<List<KeysDataModel>, int> filterRemapKeysList)
{
if (settingsRepository == null)
{
throw new ArgumentNullException(nameof(settingsRepository));
}
GeneralSettingsConfig = settingsRepository.SettingsConfig;
// set the callback functions value to hangle outgoing IPC message.
@@ -107,7 +112,22 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
public static List<AppSpecificKeysDataModel> CombineShortcutLists(List<KeysDataModel> globalShortcutList, List<AppSpecificKeysDataModel> appSpecificShortcutList)
{
return globalShortcutList.ConvertAll(x => new AppSpecificKeysDataModel { OriginalKeys = x.OriginalKeys, NewRemapKeys = x.NewRemapKeys, TargetApp = "All Apps" }).Concat(appSpecificShortcutList).ToList();
if (globalShortcutList == null && appSpecificShortcutList == null)
{
return new List<AppSpecificKeysDataModel>();
}
else if (globalShortcutList == null)
{
return appSpecificShortcutList;
}
else if (appSpecificShortcutList == null)
{
return globalShortcutList.ConvertAll(x => new AppSpecificKeysDataModel { OriginalKeys = x.OriginalKeys, NewRemapKeys = x.NewRemapKeys, TargetApp = "All Apps" }).ToList();
}
else
{
return globalShortcutList.ConvertAll(x => new AppSpecificKeysDataModel { OriginalKeys = x.OriginalKeys, NewRemapKeys = x.NewRemapKeys, TargetApp = "All Apps" }).Concat(appSpecificShortcutList).ToList();
}
}
public List<AppSpecificKeysDataModel> RemapShortcuts
@@ -129,28 +149,31 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
public ICommand EditShortcutCommand => _editShortcutCommand ?? (_editShortcutCommand = new RelayCommand(OnEditShortcut));
// Note: FxCop suggests calling ConfigureAwait() for the following methods,
// and calling ConfigureAwait(true) has the same behavior as not explicitly
// calling it (continuations are scheduled on the task-creating thread)
private async void OnRemapKeyboard()
{
await Task.Run(() => OnRemapKeyboardBackground());
await Task.Run(() => OnRemapKeyboardBackground()).ConfigureAwait(true);
}
private async void OnEditShortcut()
{
await Task.Run(() => OnEditShortcutBackground());
await Task.Run(() => OnEditShortcutBackground()).ConfigureAwait(true);
}
private async Task OnRemapKeyboardBackground()
{
Helper.AllowRunnerToForeground();
SendConfigMSG(Helper.GetSerializedCustomAction(PowerToyName, RemapKeyboardActionName, RemapKeyboardActionValue));
await Task.CompletedTask;
await Task.CompletedTask.ConfigureAwait(true);
}
private async Task OnEditShortcutBackground()
{
Helper.AllowRunnerToForeground();
SendConfigMSG(Helper.GetSerializedCustomAction(PowerToyName, EditShortcutActionName, EditShortcutActionValue));
await Task.CompletedTask;
await Task.CompletedTask.ConfigureAwait(true);
}
public void NotifyFileChanged()

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Text.Json;
using Microsoft.PowerToys.Settings.UI.Lib.Helpers;
@@ -29,6 +30,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
_settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils));
// To obtain the general Settings configurations of PowerToys
if (settingsRepository == null)
{
throw new ArgumentNullException(nameof(settingsRepository));
}
GeneralSettingsConfig = settingsRepository.SettingsConfig;
// set the callback functions value to hangle outgoing IPC message.
@@ -36,8 +42,13 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
callback = (PowerLauncherSettings settings) =>
{
// Propagate changes to Power Launcher through IPC
// Using InvariantCulture as this is an IPC message
SendConfigMSG(
string.Format("{{ \"powertoys\": {{ \"{0}\": {1} }} }}", PowerLauncherSettings.ModuleName, JsonSerializer.Serialize(settings)));
string.Format(
CultureInfo.InvariantCulture,
"{{ \"powertoys\": {{ \"{0}\": {1} }} }}",
PowerLauncherSettings.ModuleName,
JsonSerializer.Serialize(settings)));
};
if (_settingsUtils.SettingsExists(PowerLauncherSettings.ModuleName))

View File

@@ -27,10 +27,20 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
_settingsConfigFileFolder = configFileSubfolder;
// To obtain the general Settings configurations of PowerToys
if (generalSettingsRepository == null)
{
throw new ArgumentNullException(nameof(generalSettingsRepository));
}
GeneralSettingsConfig = generalSettingsRepository.SettingsConfig;
// To obtain the PowerPreview settings if it exists.
// If the file does not exist, to create a new one and return the default settings configurations.
if (moduleSettingsRepository == null)
{
throw new ArgumentNullException(nameof(moduleSettingsRepository));
}
Settings = moduleSettingsRepository.SettingsConfig;
// set the callback functions value to hangle outgoing IPC message.

View File

@@ -29,6 +29,11 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
_settingsConfigFileFolder = configFileSubfolder;
_settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils));
if (settingsRepository == null)
{
throw new ArgumentNullException(nameof(settingsRepository));
}
GeneralSettingsConfig = settingsRepository.SettingsConfig;
try

View File

@@ -27,10 +27,20 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
_settingsConfigFileFolder = configFileSubfolder;
// To obtain the general PowerToys settings.
if (settingsRepository == null)
{
throw new ArgumentNullException(nameof(settingsRepository));
}
GeneralSettingsConfig = settingsRepository.SettingsConfig;
// To obtain the shortcut guide settings, if the file exists.
// If not, to create a file with the default settings and to return the default configurations.
if (moduleSettingsRepository == null)
{
throw new ArgumentNullException(nameof(moduleSettingsRepository));
}
Settings = moduleSettingsRepository.SettingsConfig;
// set the callback functions value to hangle outgoing IPC message.
@@ -102,7 +112,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
// set theme to dark.
Settings.Properties.Theme.Value = "dark";
_themeIndex = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
if (value == 1)
@@ -110,7 +120,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
// set theme to light.
Settings.Properties.Theme.Value = "light";
_themeIndex = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
if (value == 2)
@@ -118,7 +128,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
// set theme to system default.
Settings.Properties.Theme.Value = "system";
_themeIndex = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -137,7 +147,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_pressTime = value;
Settings.Properties.PressTime.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -155,7 +165,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{
_opacity = value;
Settings.Properties.OverlayOpacity.Value = value;
RaisePropertyChanged();
NotifyPropertyChanged();
}
}
}
@@ -165,7 +175,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
return _settingsConfigFileFolder + "\\" + ModuleName;
}
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(propertyName);
SndShortcutGuideSettings outsettings = new SndShortcutGuideSettings(Settings);

View File

@@ -41,6 +41,69 @@ namespace ViewModelTests
Assert.AreEqual(expectedResult.Count, result.Count);
}
[TestMethod]
public void CombineShortcutListsShouldReturnEmptyListWhenBothArgumentsAreNull()
{
// act
var result = KeyboardManagerViewModel.CombineShortcutLists(null, null);
// Assert
var expectedResult = new List<AppSpecificKeysDataModel>();
Assert.AreEqual(expectedResult.Count, result.Count);
}
[TestMethod]
public void CombineShortcutListsShouldReturnListWithOneAppSpecificEntryWhenFirstArgumentIsNullAndSecondArgumentHasOneEntry()
{
// arrange
var secondList = new List<AppSpecificKeysDataModel>();
var entry = new AppSpecificKeysDataModel();
entry.OriginalKeys = "17;65";
entry.NewRemapKeys = "17;86";
entry.TargetApp = "msedge";
secondList.Add(entry);
// act
var result = KeyboardManagerViewModel.CombineShortcutLists(null, secondList);
// Assert
var expectedResult = new List<AppSpecificKeysDataModel>();
var expectedEntry = new AppSpecificKeysDataModel();
expectedEntry.OriginalKeys = entry.OriginalKeys;
expectedEntry.NewRemapKeys = entry.NewRemapKeys;
expectedEntry.TargetApp = entry.TargetApp;
expectedResult.Add(expectedEntry);
Assert.AreEqual(expectedResult.Count, result.Count);
Assert.IsTrue(expectedResult[0].Compare(result[0]));
}
[TestMethod]
public void CombineShortcutListsShouldReturnListWithOneAllAppsEntryWhenFirstArgumentHasOneEntryAndSecondArgumentIsNull()
{
// arrange
var firstList = new List<KeysDataModel>();
var entry = new KeysDataModel();
entry.OriginalKeys = "17;65";
entry.NewRemapKeys = "17;86";
firstList.Add(entry);
// act
var result = KeyboardManagerViewModel.CombineShortcutLists(firstList, null);
// Assert
var expectedResult = new List<AppSpecificKeysDataModel>();
var expectedEntry = new AppSpecificKeysDataModel();
expectedEntry.OriginalKeys = entry.OriginalKeys;
expectedEntry.NewRemapKeys = entry.NewRemapKeys;
expectedEntry.TargetApp = "All Apps";
expectedResult.Add(expectedEntry);
Assert.AreEqual(expectedResult.Count, result.Count);
Assert.IsTrue(expectedResult[0].Compare(result[0]));
}
[TestMethod]
public void CombineShortcutListsShouldReturnListWithOneAllAppsEntryWhenFirstArgumentHasOneEntryAndSecondArgumentIsEmpty()
{

View File

@@ -79,15 +79,15 @@ namespace Microsoft.PowerToys.Settings.UI.Views
public int UpdateUIThemeMethod(string themeName)
{
switch (themeName)
switch (themeName.ToUpperInvariant())
{
case "light":
case "LIGHT":
ShellPage.ShellHandler.RequestedTheme = ElementTheme.Light;
break;
case "dark":
case "DARK":
ShellPage.ShellHandler.RequestedTheme = ElementTheme.Dark;
break;
case "system":
case "SYSTEM":
ShellPage.ShellHandler.RequestedTheme = ElementTheme.Default;
break;
}