mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-03 09:46:54 +02:00
[Keyboard Manager] Toggle module hotkey/shortcut (#42472)
## Summary of the Pull Request Adds a keyboard shortcut to be able to toggle the Keyboard Manager module on and off. ## PR Checklist - [x] Closes: #4879 - [x] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments Modeled the changes and addition of a global shortcut using the Color Picker module. Notes: - This uses `KeyboardManagerSettings` and the associated .json settings file. I don't think there's anything else in this module that uses this. - The default key binding for this is `winkey + shift + k` - I've had to update the `KeyboardManagerViewModel` to extend `PageViewModelBase` as opposed to `Observable` to get it to work. But I will say that there were some things in here that I didn't fully dig into, so let me know if there's any potential things I'm missing. - I'm not too sure how to update the Settings UI after a hotkey is pressed (pressing the hotkey currently will not show the module being toggled off) - can't find a good way to refresh the settings ui after enabling/disabling the module. Any pointers here would be appreciated! ## Validation Steps Performed - Manually validated the following items: - Using the default shortcut (`winkey + shift + k`) - Changing the shortcut - Ensuring the change is persistent ## Media https://github.com/user-attachments/assets/e471b8df-787a-441e-b9e0-330361865c76 --------- Co-authored-by: Weike Qu <weikequ@get-stride.com> Co-authored-by: Leilei Zhang <leilzh@microsoft.com> Co-authored-by: vanzue <vanzue@outlook.com> Co-authored-by: Kai Tao <69313318+vanzue@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -21,12 +21,17 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
[CmdConfigureIgnoreAttribute]
|
||||
public GenericProperty<List<string>> KeyboardConfigurations { get; set; }
|
||||
|
||||
public HotkeySettings DefaultToggleShortcut => new HotkeySettings(true, false, false, true, 0x4B);
|
||||
|
||||
public KeyboardManagerProperties()
|
||||
{
|
||||
ToggleShortcut = DefaultToggleShortcut;
|
||||
KeyboardConfigurations = new GenericProperty<List<string>>(new List<string> { "default", });
|
||||
ActiveConfiguration = new GenericProperty<string>("default");
|
||||
}
|
||||
|
||||
public HotkeySettings ToggleShortcut { get; set; }
|
||||
|
||||
public string ToJsonString()
|
||||
{
|
||||
return JsonSerializer.Serialize(this);
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
@@ -32,5 +33,18 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public HotkeyAccessor[] GetAllHotkeyAccessors()
|
||||
{
|
||||
var hotkeyAccessors = new List<HotkeyAccessor>
|
||||
{
|
||||
new HotkeyAccessor(
|
||||
() => Properties.ToggleShortcut,
|
||||
value => Properties.ToggleShortcut = value ?? Properties.DefaultToggleShortcut,
|
||||
"Toggle_Shortcut"),
|
||||
};
|
||||
|
||||
return hotkeyAccessors.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using SettingsUILibrary = Settings.UI.Library;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.SerializationContext;
|
||||
|
||||
@@ -24,6 +23,7 @@ namespace Microsoft.PowerToys.Settings.UI.SerializationContext;
|
||||
[JsonSerializable(typeof(FileLocksmithSettings))]
|
||||
[JsonSerializable(typeof(FindMyMouseSettings))]
|
||||
[JsonSerializable(typeof(IList<PowerToysReleaseInfo>))]
|
||||
[JsonSerializable(typeof(KeyboardManagerSettings))]
|
||||
[JsonSerializable(typeof(LightSwitchSettings))]
|
||||
[JsonSerializable(typeof(MeasureToolSettings))]
|
||||
[JsonSerializable(typeof(MouseHighlighterSettings))]
|
||||
|
||||
@@ -70,6 +70,13 @@
|
||||
</tkcontrols:SettingsCard>
|
||||
</controls:GPOInfoControl>
|
||||
|
||||
<tkcontrols:SettingsCard
|
||||
Name="ToggleShortcut"
|
||||
x:Uid="KeyboardManager_Toggle_Shortcut"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
<controls:ShortcutControl MinWidth="{StaticResource SettingActionControlMinWidth}" HotkeySettings="{x:Bind Path=ViewModel.ToggleShortcut, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
|
||||
<controls:SettingsGroup x:Uid="KeyboardManager_Keys" IsEnabled="{x:Bind ViewModel.Enabled, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard
|
||||
Name="KeyboardManagerRemapKeyboardButton"
|
||||
|
||||
@@ -1876,6 +1876,12 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
|
||||
<data name="Activation_Shortcut.Description" xml:space="preserve">
|
||||
<value>Customize the shortcut to activate this module</value>
|
||||
</data>
|
||||
<data name="KeyboardManager_Toggle_Shortcut.Header" xml:space="preserve">
|
||||
<value>Toggle shortcut</value>
|
||||
</data>
|
||||
<data name="KeyboardManager_Toggle_Shortcut.Description" xml:space="preserve">
|
||||
<value>Use a shortcut to toggle this module on or off (note that the Settings UI will not update)</value>
|
||||
</data>
|
||||
<data name="PasteAsPlainText_Shortcut.Header" xml:space="preserve">
|
||||
<value>Paste as plain text directly</value>
|
||||
</data>
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
@@ -18,18 +19,20 @@ using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
|
||||
using Microsoft.PowerToys.Settings.UI.SerializationContext;
|
||||
using Microsoft.PowerToys.Settings.Utilities;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
public partial class KeyboardManagerViewModel : Observable
|
||||
public partial class KeyboardManagerViewModel : PageViewModelBase
|
||||
{
|
||||
private GeneralSettings GeneralSettingsConfig { get; set; }
|
||||
|
||||
private readonly SettingsUtils _settingsUtils;
|
||||
|
||||
private const string PowerToyName = KeyboardManagerSettings.ModuleName;
|
||||
protected override string ModuleName => KeyboardManagerSettings.ModuleName;
|
||||
|
||||
private const string JsonFileType = ".json";
|
||||
|
||||
// Default editor path. Can be removed once the new WinUI3 editor is released.
|
||||
@@ -74,15 +77,15 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
_settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils));
|
||||
|
||||
if (_settingsUtils.SettingsExists(PowerToyName))
|
||||
if (_settingsUtils.SettingsExists(ModuleName))
|
||||
{
|
||||
try
|
||||
{
|
||||
Settings = _settingsUtils.GetSettingsOrDefault<KeyboardManagerSettings>(PowerToyName);
|
||||
Settings = _settingsUtils.GetSettingsOrDefault<KeyboardManagerSettings>(ModuleName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"Exception encountered while reading {PowerToyName} settings.", e);
|
||||
Logger.LogError($"Exception encountered while reading {ModuleName} settings.", e);
|
||||
#if DEBUG
|
||||
if (e is ArgumentException || e is ArgumentNullException || e is PathTooLongException)
|
||||
{
|
||||
@@ -100,7 +103,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
else
|
||||
{
|
||||
Settings = new KeyboardManagerSettings();
|
||||
_settingsUtils.SaveSettings(Settings.ToJsonString(), PowerToyName);
|
||||
_settingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +177,41 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public override Dictionary<string, HotkeySettings[]> GetAllHotkeySettings()
|
||||
{
|
||||
var hotkeysDict = new Dictionary<string, HotkeySettings[]>
|
||||
{
|
||||
[ModuleName] = [ToggleShortcut],
|
||||
};
|
||||
|
||||
return hotkeysDict;
|
||||
}
|
||||
|
||||
public HotkeySettings ToggleShortcut
|
||||
{
|
||||
get => Settings.Properties.ToggleShortcut;
|
||||
set
|
||||
{
|
||||
if (Settings.Properties.ToggleShortcut != value)
|
||||
{
|
||||
Settings.Properties.ToggleShortcut = value ?? Settings.Properties.DefaultToggleShortcut;
|
||||
OnPropertyChanged(nameof(ToggleShortcut));
|
||||
NotifySettingsChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void NotifySettingsChanged()
|
||||
{
|
||||
// Using InvariantCulture as this is an IPC message
|
||||
SendConfigMSG(
|
||||
string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{{ \"powertoys\": {{ \"{0}\": {1} }} }}",
|
||||
ModuleName,
|
||||
JsonSerializer.Serialize(Settings, SourceGenerationContextContext.Default.KeyboardManagerSettings)));
|
||||
}
|
||||
|
||||
public static List<AppSpecificKeysDataModel> CombineShortcutLists(List<KeysDataModel> globalShortcutList, List<AppSpecificKeysDataModel> appSpecificShortcutList)
|
||||
{
|
||||
string allAppsDescription = "All Apps";
|
||||
@@ -256,13 +294,13 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
if (editor != null && editor.HasExited)
|
||||
{
|
||||
Logger.LogInfo($"Previous instance of {PowerToyName} editor exited");
|
||||
Logger.LogInfo($"Previous instance of {ModuleName} editor exited");
|
||||
editor = null;
|
||||
}
|
||||
|
||||
if (editor != null)
|
||||
{
|
||||
Logger.LogInfo($"The {PowerToyName} editor instance {editor.Id} exists. Bringing the process to the front");
|
||||
Logger.LogInfo($"The {ModuleName} editor instance {editor.Id} exists. Bringing the process to the front");
|
||||
BringProcessToFront(editor);
|
||||
return;
|
||||
}
|
||||
@@ -298,14 +336,14 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
|
||||
string path = Path.Combine(Environment.CurrentDirectory, editorPath);
|
||||
Logger.LogInfo($"Starting {PowerToyName} editor from {path}");
|
||||
Logger.LogInfo($"Starting {ModuleName} editor from {path}");
|
||||
|
||||
// InvariantCulture: type represents the KeyboardManagerEditorType enum value
|
||||
editor = Process.Start(path, $"{type.ToString(CultureInfo.InvariantCulture)} {Environment.ProcessId}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"Exception encountered when opening an {PowerToyName} editor", e);
|
||||
Logger.LogError($"Exception encountered when opening an {ModuleName} editor", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,7 +376,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
while (!readSuccessfully && !ts.IsCancellationRequested)
|
||||
{
|
||||
profileExists = _settingsUtils.SettingsExists(PowerToyName, fileName);
|
||||
profileExists = _settingsUtils.SettingsExists(ModuleName, fileName);
|
||||
if (!profileExists)
|
||||
{
|
||||
break;
|
||||
@@ -347,12 +385,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
try
|
||||
{
|
||||
_profile = _settingsUtils.GetSettingsOrDefault<KeyboardManagerProfile>(PowerToyName, fileName);
|
||||
_profile = _settingsUtils.GetSettingsOrDefault<KeyboardManagerProfile>(ModuleName, fileName);
|
||||
readSuccessfully = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"Exception encountered when reading {PowerToyName} settings", e);
|
||||
Logger.LogError($"Exception encountered when reading {ModuleName} settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,23 +417,23 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
if (!completedInTime)
|
||||
{
|
||||
Logger.LogError($"Timeout encountered when loading {PowerToyName} profile");
|
||||
Logger.LogError($"Timeout encountered when loading {ModuleName} profile");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Failed to load the configuration.
|
||||
Logger.LogError($"Exception encountered when loading {PowerToyName} profile", e);
|
||||
Logger.LogError($"Exception encountered when loading {ModuleName} profile", e);
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (!profileExists)
|
||||
{
|
||||
Logger.LogInfo($"Couldn't load {PowerToyName} profile because it doesn't exist");
|
||||
Logger.LogInfo($"Couldn't load {ModuleName} profile because it doesn't exist");
|
||||
}
|
||||
else if (!success)
|
||||
{
|
||||
Logger.LogError($"Couldn't load {PowerToyName} profile");
|
||||
Logger.LogError($"Couldn't load {ModuleName} profile");
|
||||
}
|
||||
|
||||
return success;
|
||||
|
||||
Reference in New Issue
Block a user