mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-04 10:16:24 +02:00
## Summary of the Pull Request Enables the users to choose what left, right, and middle click does when color picker is open. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] **Closes:** #39006 - [x] **Communication:** I've discussed this with core contributors already - [x] **Tests:** All pass - [x] **Localization:** All end user facing strings can be localized - [x] **Dev docs:** No need - [x] **New binaries:** None - [x] **Documentation updated:** No need ## Detailed Description of the Pull Request / Additional comments  Adds option to choose from 3 click behaviors for each standard mouse button: * **Pick color and open editor**: Copies color to clipboard, saves it to the color history and opens the editor * **Pick color and close**: Copies color to clipboard, saves it to the color history and exits * **Close**: Closes color picker without copying the color Pressing <kbd>Enter</kbd> or <kbd>Space</kbd> does what clicking the primary button would. Left and middle click actions execute on mouse down, right click on mouse up for reasons discussed previously (I can't find the conversation now) Default settings are chosen in such a way that very little or nothing changes by updating. ## Validation Steps Performed Tested: * Migrating settings from v2.0 to v2.1 (v1 to v2.1 not tested) * Settings page displays and saves settings correctly * Default settings load correctly * All three click actions do what they are supposed to * Activation behavior works as expected, doesn't affect click actions
231 lines
11 KiB
C#
231 lines
11 KiB
C#
// Copyright (c) Microsoft Corporation
|
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
// See the LICENSE file in the project root for more information.
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.IO.Abstractions;
|
|
using System.Text.Json;
|
|
|
|
using ManagedCommon;
|
|
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
|
|
|
namespace Microsoft.PowerToys.Settings.UI.Library
|
|
{
|
|
public class SettingsUtils : ISettingsUtils
|
|
{
|
|
public const string DefaultFileName = "settings.json";
|
|
private const string DefaultModuleName = "";
|
|
private readonly IFile _file;
|
|
private readonly ISettingsPath _settingsPath;
|
|
|
|
private static readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions
|
|
{
|
|
MaxDepth = 0,
|
|
IncludeFields = true,
|
|
};
|
|
|
|
public SettingsUtils()
|
|
: this(new FileSystem())
|
|
{
|
|
}
|
|
|
|
public SettingsUtils(IFileSystem fileSystem)
|
|
: this(fileSystem?.File, new SettingPath(fileSystem?.Directory, fileSystem?.Path))
|
|
{
|
|
}
|
|
|
|
public SettingsUtils(IFile file, ISettingsPath settingPath)
|
|
{
|
|
_file = file ?? throw new ArgumentNullException(nameof(file));
|
|
_settingsPath = settingPath;
|
|
}
|
|
|
|
public bool SettingsExists(string powertoy = DefaultModuleName, string fileName = DefaultFileName)
|
|
{
|
|
var settingsPath = _settingsPath.GetSettingsPath(powertoy, fileName);
|
|
return _file.Exists(settingsPath);
|
|
}
|
|
|
|
public void DeleteSettings(string powertoy = "")
|
|
{
|
|
_settingsPath.DeleteSettings(powertoy);
|
|
}
|
|
|
|
public T GetSettings<T>(string powertoy = DefaultModuleName, string fileName = DefaultFileName)
|
|
where T : ISettingsConfig, new()
|
|
{
|
|
if (!SettingsExists(powertoy, fileName))
|
|
{
|
|
throw new FileNotFoundException();
|
|
}
|
|
|
|
// Given the file already exists, to deserialize the file and read its content.
|
|
T deserializedSettings = GetFile<T>(powertoy, fileName);
|
|
|
|
// If the file needs to be modified, to save the new configurations accordingly.
|
|
if (deserializedSettings.UpgradeSettingsConfiguration())
|
|
{
|
|
SaveSettings(deserializedSettings.ToJsonString(), powertoy, fileName);
|
|
}
|
|
|
|
return deserializedSettings;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a Deserialized object of the json settings string.
|
|
/// This function creates a file in the powertoy folder if it does not exist and returns an object with default properties.
|
|
/// </summary>
|
|
/// <returns>Deserialized json settings object.</returns>
|
|
public T GetSettingsOrDefault<T>(string powertoy = DefaultModuleName, string fileName = DefaultFileName)
|
|
where T : ISettingsConfig, new()
|
|
{
|
|
try
|
|
{
|
|
return GetSettings<T>(powertoy, fileName);
|
|
}
|
|
|
|
// Catch json deserialization exceptions when the file is corrupt and has an invalid json.
|
|
// If there are any deserialization issues like in https://github.com/microsoft/PowerToys/issues/7500, log the error and create a new settings.json file.
|
|
// This is different from the case where we have trailing zeros following a valid json file, which we have handled by trimming the trailing zeros.
|
|
catch (JsonException ex)
|
|
{
|
|
Logger.LogError($"Exception encountered while loading {powertoy} settings.", ex);
|
|
}
|
|
catch (FileNotFoundException)
|
|
{
|
|
Logger.LogInfo($"Settings file {fileName} for {powertoy} was not found.");
|
|
}
|
|
|
|
// If the settings file does not exist or if the file is corrupt, to create a new object with default parameters and save it to a newly created settings file.
|
|
T newSettingsItem = new T();
|
|
SaveSettings(newSettingsItem.ToJsonString(), powertoy, fileName);
|
|
return newSettingsItem;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a Deserialized object of the json settings string.
|
|
/// This function creates a file in the powertoy folder if it does not exist and returns an object with default properties.
|
|
/// </summary>
|
|
/// <returns>Deserialized json settings object.</returns>
|
|
public T GetSettingsOrDefault<T, T2>(string powertoy = DefaultModuleName, string fileName = DefaultFileName, Func<object, object> settingsUpgrader = null)
|
|
where T : ISettingsConfig, new()
|
|
where T2 : ISettingsConfig, new()
|
|
{
|
|
try
|
|
{
|
|
return GetSettings<T>(powertoy, fileName);
|
|
}
|
|
|
|
// Catch json deserialization exceptions when the file is corrupt and has an invalid json.
|
|
// If there are any deserialization issues like in https://github.com/microsoft/PowerToys/issues/7500, log the error and create a new settings.json file.
|
|
// This is different from the case where we have trailing zeros following a valid json file, which we have handled by trimming the trailing zeros.
|
|
catch (JsonException ex)
|
|
{
|
|
Logger.LogInfo($"Settings file {fileName} for {powertoy} was unrecognized. Possibly containing an older version. Trying to read again.");
|
|
|
|
// try to deserialize to the old format, which is presented in T2
|
|
try
|
|
{
|
|
T2 oldSettings = GetSettings<T2>(powertoy, fileName);
|
|
T newSettings = (T)settingsUpgrader(oldSettings);
|
|
Logger.LogInfo($"Settings file {fileName} for {powertoy} was read successfully in the old format.");
|
|
|
|
// If the file needs to be modified, to save the new configurations accordingly.
|
|
if (newSettings.UpgradeSettingsConfiguration())
|
|
{
|
|
SaveSettings(newSettings.ToJsonString(), powertoy, fileName);
|
|
}
|
|
|
|
return newSettings;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// do nothing, the problem wasn't that the settings was stored in the previous format, continue with the default settings
|
|
Logger.LogError($"{powertoy} settings are corrupt or the format is not supported any longer. Using default settings instead.", ex);
|
|
}
|
|
}
|
|
catch (FileNotFoundException)
|
|
{
|
|
Logger.LogInfo($"Settings file {fileName} for {powertoy} was not found.");
|
|
}
|
|
|
|
// If the settings file does not exist or if the file is corrupt, to create a new object with default parameters and save it to a newly created settings file.
|
|
T newSettingsItem = new T();
|
|
SaveSettings(newSettingsItem.ToJsonString(), powertoy, fileName);
|
|
return newSettingsItem;
|
|
}
|
|
|
|
// Given the powerToy folder name and filename to be accessed, this function deserializes and returns the file.
|
|
private T GetFile<T>(string powertoyFolderName = DefaultModuleName, string fileName = DefaultFileName)
|
|
{
|
|
// Adding Trim('\0') to overcome possible NTFS file corruption.
|
|
// Look at issue https://github.com/microsoft/PowerToys/issues/6413 you'll see the file has a large sum of \0 to fill up a 4096 byte buffer for writing to disk
|
|
// This, while not totally ideal, does work around the problem by trimming the end.
|
|
// The file itself did write the content correctly but something is off with the actual end of the file, hence the 0x00 bug
|
|
var jsonSettingsString = _file.ReadAllText(_settingsPath.GetSettingsPath(powertoyFolderName, fileName)).Trim('\0');
|
|
|
|
var options = _serializerOptions;
|
|
return JsonSerializer.Deserialize<T>(jsonSettingsString, options);
|
|
}
|
|
|
|
// Save settings to a json file.
|
|
public void SaveSettings(string jsonSettings, string powertoy = DefaultModuleName, string fileName = DefaultFileName)
|
|
{
|
|
try
|
|
{
|
|
if (jsonSettings != null)
|
|
{
|
|
if (!_settingsPath.SettingsFolderExists(powertoy))
|
|
{
|
|
_settingsPath.CreateSettingsFolder(powertoy);
|
|
}
|
|
|
|
_file.WriteAllText(_settingsPath.GetSettingsPath(powertoy, fileName), jsonSettings);
|
|
}
|
|
}
|
|
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
|
|
}
|
|
}
|
|
|
|
// Returns the file path to the settings file, that is exposed from the local ISettingsPath instance.
|
|
public string GetSettingsFilePath(string powertoy = "", string fileName = "settings.json")
|
|
{
|
|
return _settingsPath.GetSettingsPath(powertoy, fileName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method <c>BackupSettings</c> Mostly a wrapper for SettingsBackupAndRestoreUtils.BackupSettings
|
|
/// </summary>
|
|
public static (bool Success, string Message, string Severity, bool LastBackupExists, string OptionalMessage) BackupSettings()
|
|
{
|
|
var settingsBackupAndRestoreUtilsX = SettingsBackupAndRestoreUtils.Instance;
|
|
var settingsUtils = new SettingsUtils();
|
|
var appBasePath = Path.GetDirectoryName(settingsUtils._settingsPath.GetSettingsPath(string.Empty, string.Empty));
|
|
string settingsBackupAndRestoreDir = settingsBackupAndRestoreUtilsX.GetSettingsBackupAndRestoreDir();
|
|
|
|
return settingsBackupAndRestoreUtilsX.BackupSettings(appBasePath, settingsBackupAndRestoreDir, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method <c>RestoreSettings</c> Mostly a wrapper for SettingsBackupAndRestoreUtils.RestoreSettings
|
|
/// </summary>
|
|
public static (bool Success, string Message, string Severity) RestoreSettings()
|
|
{
|
|
var settingsBackupAndRestoreUtilsX = SettingsBackupAndRestoreUtils.Instance;
|
|
var settingsUtils = new SettingsUtils();
|
|
var appBasePath = Path.GetDirectoryName(settingsUtils._settingsPath.GetSettingsPath(string.Empty, string.Empty));
|
|
string settingsBackupAndRestoreDir = settingsBackupAndRestoreUtilsX.GetSettingsBackupAndRestoreDir();
|
|
return settingsBackupAndRestoreUtilsX.RestoreSettings(appBasePath, settingsBackupAndRestoreDir);
|
|
}
|
|
}
|
|
}
|