Integrate keyboard manager with settings v2[Part-2] (#2107)
* Added CustomAction Data Models * Updated data model * Updated Button command trigger for keyboard manager * Integerated custom actions in Keyboard manager module backend * Allow Runner to launch windows on foreground * Refactor and moved the execution logic to background thread * Removed non-required unlock calls * Fixed typo * Resolve PR comments
This commit is contained in:
parent
79fcd45d5f
commit
a88d66b48b
13 changed files with 292 additions and 40 deletions
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Lib.CustomAction
|
||||
{
|
||||
public class CustomActionDataModel
|
||||
{
|
||||
[JsonPropertyName("action_name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("value")]
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Lib.CustomAction
|
||||
{
|
||||
public class CustomNamePolicy : JsonNamingPolicy
|
||||
{
|
||||
private Func<string, string> convertDelegate;
|
||||
|
||||
public CustomNamePolicy(Func<string, string> convertDelegate)
|
||||
{
|
||||
this.convertDelegate = convertDelegate;
|
||||
}
|
||||
|
||||
public override string ConvertName(string name)
|
||||
{
|
||||
return convertDelegate(name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Lib.CustomAction
|
||||
{
|
||||
public class ModuleCustomAction
|
||||
{
|
||||
public CustomActionDataModel ModuleAction { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// 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.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Lib.CustomAction
|
||||
{
|
||||
public class SendCustomAction
|
||||
{
|
||||
private readonly string moduleName;
|
||||
|
||||
public SendCustomAction(string moduleName)
|
||||
{
|
||||
this.moduleName = moduleName;
|
||||
}
|
||||
|
||||
[JsonPropertyName("action")]
|
||||
public ModuleCustomAction Action { get; set; }
|
||||
|
||||
public string ToJsonString()
|
||||
{
|
||||
var jsonSerializerOptions = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = new CustomNamePolicy((propertyName) =>
|
||||
{
|
||||
return propertyName.Equals("ModuleAction") ? moduleName : propertyName;
|
||||
}),
|
||||
};
|
||||
|
||||
return JsonSerializer.Serialize(this, jsonSerializerOptions);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.PowerToys.Settings.UI.Lib.CustomAction;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Lib.Utilities
|
||||
{
|
||||
public class Helper
|
||||
{
|
||||
public static bool AllowRunnerToForeground()
|
||||
{
|
||||
var result = false;
|
||||
var processes = Process.GetProcessesByName("PowerToys");
|
||||
if (processes.Length > 0)
|
||||
{
|
||||
var pid = processes[0].Id;
|
||||
result = AllowSetForegroundWindow(pid);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string GetSerializedCustomAction(string moduleName, string actionName, string actionValue)
|
||||
{
|
||||
var customAction = new CustomActionDataModel
|
||||
{
|
||||
Name = actionName,
|
||||
Value = actionValue,
|
||||
};
|
||||
|
||||
var moduleCustomAction = new ModuleCustomAction
|
||||
{
|
||||
ModuleAction = customAction,
|
||||
};
|
||||
|
||||
var sendCustomAction = new SendCustomAction(moduleName);
|
||||
sendCustomAction.Action = moduleCustomAction;
|
||||
return sendCustomAction.ToJsonString();
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool AllowSetForegroundWindow(int dwProcessId);
|
||||
}
|
||||
}
|
|
@ -2,14 +2,49 @@
|
|||
// 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.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Lib.Utilities;
|
||||
using Microsoft.PowerToys.Settings.UI.Views;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
public class KeyboardManagerViewModel : Observable
|
||||
{
|
||||
private ICommand remapKeyboardCommand;
|
||||
private ICommand editShortcutCommand;
|
||||
|
||||
public ICommand RemapKeyboardCommand => remapKeyboardCommand ?? (remapKeyboardCommand = new RelayCommand(OnRemapKeyboard));
|
||||
|
||||
public ICommand EditShortcutCommand => editShortcutCommand ?? (editShortcutCommand = new RelayCommand(OnEditShortcut));
|
||||
|
||||
public KeyboardManagerViewModel()
|
||||
{
|
||||
}
|
||||
|
||||
private async void OnRemapKeyboard()
|
||||
{
|
||||
await Task.Run(() => OnRemapKeyboardBackground());
|
||||
}
|
||||
|
||||
private async void OnEditShortcut()
|
||||
{
|
||||
await Task.Run(() => OnEditShortcutBackground());
|
||||
}
|
||||
|
||||
private async Task OnRemapKeyboardBackground()
|
||||
{
|
||||
Helper.AllowRunnerToForeground();
|
||||
ShellPage.DefaultSndMSGCallback(Helper.GetSerializedCustomAction("Keyboard Manager", "RemapKeyboard", "Create Remap Keyboard Window"));
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task OnEditShortcutBackground()
|
||||
{
|
||||
Helper.AllowRunnerToForeground();
|
||||
ShellPage.DefaultSndMSGCallback(Helper.GetSerializedCustomAction("Keyboard Manager", "EditShortcut", "Create Edit Shortcut Window"));
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,7 +122,8 @@
|
|||
|
||||
<Button x:Uid="KeyboardManager_RemapKeyboardButton"
|
||||
Margin="{StaticResource SmallTopMargin}"
|
||||
Style="{StaticResource ButtonRevealStyle}"/>
|
||||
Style="{StaticResource ButtonRevealStyle}"
|
||||
Command="{Binding Path=RemapKeyboardCommand}"/>
|
||||
|
||||
<ListView x:Name="RemapKeysList"
|
||||
ItemsSource="{StaticResource dummyData}"
|
||||
|
@ -143,7 +144,8 @@
|
|||
|
||||
<Button x:Uid="KeyboardManager_RemapShortcutsButton"
|
||||
Margin="{StaticResource SmallTopMargin}"
|
||||
Style="{StaticResource ButtonRevealStyle}"/>
|
||||
Style="{StaticResource ButtonRevealStyle}"
|
||||
Command="{Binding Path=EditShortcutCommand}"/>
|
||||
|
||||
<ListView x:Name="RemapShortcutsList"
|
||||
ItemsSource="{StaticResource dummyData}"
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
|||
public KeyboardManagerPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = ViewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
#include <common/settings_objects.h>
|
||||
#include "trace.h"
|
||||
#include "resource.h"
|
||||
#include <keyboardmanager/ui/MainWindow.h>
|
||||
#include <keyboardmanager/ui/EditKeyboardWindow.h>
|
||||
#include <keyboardmanager/ui/EditShortcutsWindow.h>
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/common/Shortcut.h>
|
||||
#include <keyboardmanager/common/RemapShortcut.h>
|
||||
|
@ -149,10 +150,22 @@ public:
|
|||
// Parse the action values, including name.
|
||||
PowerToysSettings::CustomActionObject action_object =
|
||||
PowerToysSettings::CustomActionObject::from_json_string(action);
|
||||
HINSTANCE hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
|
||||
|
||||
//if (action_object.get_name() == L"custom_action_id") {
|
||||
// // Execute your custom action
|
||||
//}
|
||||
if (action_object.get_name() == L"RemapKeyboard")
|
||||
{
|
||||
if (!CheckEditKeyboardWindowActive())
|
||||
{
|
||||
std::thread(createEditKeyboardWindow, hInstance, std::ref(keyboardManagerState)).detach();
|
||||
}
|
||||
}
|
||||
else if (action_object.get_name() == L"EditShortcut")
|
||||
{
|
||||
if (!CheckEditShortcutsWindowActive())
|
||||
{
|
||||
std::thread(createEditShortcutsWindow, hInstance, std::ref(keyboardManagerState)).detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
|
@ -183,8 +196,6 @@ public:
|
|||
virtual void enable()
|
||||
{
|
||||
m_enabled = true;
|
||||
HINSTANCE hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
|
||||
std::thread(createMainWindow, hInstance, std::ref(keyboardManagerState)).detach();
|
||||
start_lowlevel_keyboard_hook();
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND, UINT, WPARAM, LPARAM);
|
|||
HWND hWndXamlIslandEditKeyboardWindow = nullptr;
|
||||
// This variable is used to check if window registration has been done to avoid repeated registration leading to an error.
|
||||
bool isEditKeyboardWindowRegistrationCompleted = false;
|
||||
// Holds the native window handle of EditKeyboard Window.
|
||||
HWND hwndEditKeyboardNativeWindow = nullptr;
|
||||
std::mutex editKeyboardWindowMutex;
|
||||
|
||||
// Function to create the Edit Keyboard Window
|
||||
void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
|
||||
|
@ -52,6 +55,11 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||
return;
|
||||
}
|
||||
|
||||
// Store the newly created Edit Keyboard window's handle.
|
||||
std::unique_lock<std::mutex> hwndLock(editKeyboardWindowMutex);
|
||||
hwndEditKeyboardNativeWindow = _hWndEditKeyboardWindow;
|
||||
hwndLock.unlock();
|
||||
|
||||
// This DesktopWindowXamlSource is the object that enables a non-UWP desktop application
|
||||
// to host UWP controls in any UI element that is associated with a window handle (HWND).
|
||||
DesktopWindowXamlSource desktopSource;
|
||||
|
@ -227,6 +235,10 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||
DispatchMessage(&msg);
|
||||
}
|
||||
desktopSource.Close();
|
||||
|
||||
hWndXamlIslandEditKeyboardWindow = nullptr;
|
||||
hwndLock.lock();
|
||||
hwndEditKeyboardNativeWindow = nullptr;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
|
||||
|
@ -248,3 +260,22 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wPar
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CheckEditKeyboardWindowActive()
|
||||
{
|
||||
bool result = false;
|
||||
std::unique_lock<std::mutex> hwndLock(editKeyboardWindowMutex);
|
||||
if (hwndEditKeyboardNativeWindow != nullptr)
|
||||
{
|
||||
// Check if the window is minimized if yes then restore the window.
|
||||
if (IsIconic(hwndEditKeyboardNativeWindow))
|
||||
{
|
||||
ShowWindow(hwndEditKeyboardNativeWindow, SW_RESTORE);
|
||||
}
|
||||
// If there is an already existing window no need to create a new open bring it on foreground.
|
||||
SetForegroundWindow(hwndEditKeyboardNativeWindow);
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -3,3 +3,6 @@
|
|||
|
||||
// Function to create the Edit Keyboard Window
|
||||
__declspec(dllexport) void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState);
|
||||
|
||||
// Function to check if there is already a window active if yes bring to foreground.
|
||||
__declspec(dllexport) bool CheckEditKeyboardWindowActive();
|
|
@ -8,6 +8,9 @@ LRESULT CALLBACK EditShortcutsWindowProc(HWND, UINT, WPARAM, LPARAM);
|
|||
HWND hWndXamlIslandEditShortcutsWindow = nullptr;
|
||||
// This variable is used to check if window registration has been done to avoid repeated registration leading to an error.
|
||||
bool isEditShortcutsWindowRegistrationCompleted = false;
|
||||
// Holds the native window handle of EditShortcuts Window.
|
||||
HWND hwndEditShortcutsNativeWindow = nullptr;
|
||||
std::mutex editShortcutsWindowMutex;
|
||||
|
||||
// Function to create the Edit Shortcuts Window
|
||||
void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
|
||||
|
@ -52,6 +55,11 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
|||
return;
|
||||
}
|
||||
|
||||
// Store the newly created Edit Shortcuts window's handle.
|
||||
std::unique_lock<std::mutex> hwndLock(editShortcutsWindowMutex);
|
||||
hwndEditShortcutsNativeWindow = _hWndEditShortcutsWindow;
|
||||
hwndLock.unlock();
|
||||
|
||||
// This DesktopWindowXamlSource is the object that enables a non-UWP desktop application
|
||||
// to host UWP controls in any UI element that is associated with a window handle (HWND).
|
||||
DesktopWindowXamlSource desktopSource;
|
||||
|
@ -213,6 +221,10 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
|||
DispatchMessage(&msg);
|
||||
}
|
||||
desktopSource.Close();
|
||||
|
||||
hWndXamlIslandEditShortcutsWindow = nullptr;
|
||||
hwndLock.lock();
|
||||
hwndEditShortcutsNativeWindow = nullptr;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
|
||||
|
@ -234,3 +246,23 @@ LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wPa
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CheckEditShortcutsWindowActive()
|
||||
{
|
||||
bool result = false;
|
||||
std::unique_lock<std::mutex> hwndLock(editShortcutsWindowMutex);
|
||||
if (hwndEditShortcutsNativeWindow != nullptr)
|
||||
{
|
||||
// Check if the window is minimized if yes then restore the window.
|
||||
if (IsIconic(hwndEditShortcutsNativeWindow))
|
||||
{
|
||||
ShowWindow(hwndEditShortcutsNativeWindow, SW_RESTORE);
|
||||
}
|
||||
|
||||
// If there is an already existing window no need to create a new open bring it on foreground.
|
||||
SetForegroundWindow(hwndEditShortcutsNativeWindow);
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -5,3 +5,6 @@
|
|||
|
||||
// Function to create the Edit Shortcuts Window
|
||||
__declspec(dllexport) void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState);
|
||||
|
||||
// Function to check if there is already a window active if yes bring to foreground.
|
||||
__declspec(dllexport) bool CheckEditShortcutsWindowActive();
|
Loading…
Reference in a new issue