diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Pages/Remappings.xaml.cs b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Pages/Remappings.xaml.cs index deca17bbd6..9cdf4769ee 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Pages/Remappings.xaml.cs +++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Pages/Remappings.xaml.cs @@ -155,10 +155,14 @@ namespace KeyboardManagerEditorUI.Pages RemappingControl.SetRemappedKeys(new List()); RemappingControl.SetApp(false, string.Empty); + RemappingControl.SetKeyboardHook(); + // Show the dialog to add a new remapping KeyDialog.PrimaryButtonClick += KeyDialog_PrimaryButtonClick; await KeyDialog.ShowAsync(); KeyDialog.PrimaryButtonClick -= KeyDialog_PrimaryButtonClick; + + RemappingControl.CleanupKeyboardHook(); } private void KeyDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) @@ -175,9 +179,13 @@ namespace KeyboardManagerEditorUI.Pages RemappingControl.SetRemappedKeys(selectedRemapping.RemappedKeys); RemappingControl.SetApp(!selectedRemapping.IsAllApps, selectedRemapping.AppName); + RemappingControl.SetKeyboardHook(); + KeyDialog.PrimaryButtonClick += KeyDialog_PrimaryButtonClick; await KeyDialog.ShowAsync(); KeyDialog.PrimaryButtonClick -= KeyDialog_PrimaryButtonClick; + + RemappingControl.CleanupKeyboardHook(); } } diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/InputControl.xaml b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/InputControl.xaml index a19af7e895..66b671ef2a 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/InputControl.xaml +++ b/src/modules/keyboardmanager/KeyboardManagerEditorUI/Styles/InputControl.xaml @@ -51,14 +51,6 @@ - - - - - - - - pressedKeys = new List(); - private List newPressedKeys = new List(); + // List to store pressed keys + private HashSet _currentlyPressedKeys = new HashSet(); + + // List to store order of remapped keys + private List _keyPressOrder = new List(); + + // Collection to store original and remapped keys + private ObservableCollection _originalKeys = new ObservableCollection(); + private ObservableCollection _remappedKeys = new ObservableCollection(); + + private HotkeySettingsControlHook? _keyboardHook; + private bool _disposed; // Define newMode as a DependencyProperty for binding public static readonly DependencyProperty NewModeProperty = @@ -33,127 +47,192 @@ namespace KeyboardManagerEditorUI.Styles public InputControl() { this.InitializeComponent(); - this.KeyDown += (sender, e) => InputControl_KeyDown(sender, e, NewMode); - this.KeyUp += (sender, e) => InputControl_KeyUp(sender, e, NewMode); + + this.OriginalKeys.ItemsSource = _originalKeys; + this.RemappedKeys.ItemsSource = _remappedKeys; + } + + private void InputControl_KeyDown(int key) + { + // if no keys are pressed, clear the lists when a new key is pressed + if (_currentlyPressedKeys.Count == 0) + { + if (NewMode) + { + _remappedKeys.Clear(); + } + else + { + _originalKeys.Clear(); + } + + _keyPressOrder.Clear(); + } + + VirtualKey virtualKey = (VirtualKey)key; + + if (_currentlyPressedKeys.Add(virtualKey)) + { + _keyPressOrder.Add(virtualKey); + UpdateKeysDisplay(); + } + } + + private void InputControl_KeyUp(int key) + { + VirtualKey virtualKey = (VirtualKey)key; + + if (_currentlyPressedKeys.Remove(virtualKey)) + { + _keyPressOrder.Remove(virtualKey); + } } private static void OnNewModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - var control = d as InputControl; - if (control != null) + if (d is InputControl control) { - bool newMode = (bool)e.NewValue; - control.UpdateKeyDisplay(newMode); + // Clear the lists when the mode changes + control._currentlyPressedKeys.Clear(); + control._keyPressOrder.Clear(); } } - private void InputControl_KeyDown(object sender, KeyRoutedEventArgs e, bool newMode) + public void SetKeyboardHook() { - // Get the key name and add it to the list if it's not already there - string keyName = e.Key.ToString(); - keyName = NormalizeKeyName(keyName); + CleanupKeyboardHook(); - var currentKeyList = newMode ? newPressedKeys : pressedKeys; + _keyboardHook = new HotkeySettingsControlHook( + InputControl_KeyDown, + InputControl_KeyUp, + () => true, + (key, extraInfo) => true); + } - if (!currentKeyList.Contains(keyName)) + public void CleanupKeyboardHook() + { + if (_keyboardHook != null) { - var allowedModifiers = new[] { "Shift", "Ctrl", "LWin", "RWin", "Alt" }; - if (currentKeyList.All(k => allowedModifiers.Contains(k)) && pressedKeys.Count < 4) + _keyboardHook.Dispose(); + _keyboardHook = null; + } + } + + private void UpdateKeysDisplay() + { + var formattedKeys = GetFormattedKeyList(); + + if (NewMode) + { + _remappedKeys.Clear(); + foreach (var key in formattedKeys) { - currentKeyList.Add(keyName); - UpdateKeyDisplay(newMode); + _remappedKeys.Add(key); + } + } + else + { + _originalKeys.Clear(); + foreach (var key in formattedKeys) + { + _originalKeys.Add(key); } } } - private void InputControl_KeyUp(object sender, KeyRoutedEventArgs e, bool newMode) + private List GetFormattedKeyList() { - // Console.WriteLine(newMode); - string keyName = e.Key.ToString(); - var currentKeyList = newMode ? newPressedKeys : pressedKeys; + List keyList = new List(); + List modifierKeys = new List(); + VirtualKey? actionKey = null; - if (!currentKeyList.Contains(keyName)) + foreach (var key in _keyPressOrder) { - return; - } - - // Remove the key name from the list when the key is released - // currentKeyList.Remove(keyName); - UpdateKeyDisplay(newMode); - } - - private void UpdateKeyDisplay(bool newMode) - { - // Clear current UI elements - if (newMode) - { - NewKeyStackPanel.Children.Clear(); - } // Assuming keyPanel2 is your second stack panel - else - { - KeyStackPanel.Children.Clear(); - } - - var currentKeyList = newMode ? newPressedKeys : pressedKeys; - - // Add each pressed key as a TextBlock in the StackPanel - foreach (var key in currentKeyList) - { - Border keyBlockContainer = new Border + if (!_currentlyPressedKeys.Contains(key)) { - Background = new SolidColorBrush(Microsoft.UI.Colors.White), - Padding = new Thickness(10), - Margin = new Thickness(1), - CornerRadius = new CornerRadius(3), - BorderBrush = new SolidColorBrush(Microsoft.UI.Colors.Black), - BorderThickness = new Thickness(1), - }; + continue; + } - TextBlock keyBlock = new TextBlock + if (IsModifierKey(key)) { - Text = key, - FontSize = 12, - VerticalAlignment = VerticalAlignment.Center, - HorizontalAlignment = HorizontalAlignment.Center, - Foreground = new SolidColorBrush(Microsoft.UI.Colors.Black), - }; - - // Add TextBlock inside the Border container - keyBlockContainer.Child = keyBlock; - - // Add Border to StackPanel - if (newMode) - { - keyBlockContainer.Background = Application.Current.Resources["AccentButtonBackgroundPressed"] as SolidColorBrush; - keyBlockContainer.BorderBrush = Application.Current.Resources["AccentButtonBackgroundPressed"] as SolidColorBrush; - keyBlock.Foreground = new SolidColorBrush(Microsoft.UI.Colors.White); - NewKeyStackPanel.Children.Add(keyBlockContainer); // For remapping keys + if (!modifierKeys.Contains(key)) + { + modifierKeys.Add(key); + } } else { - KeyStackPanel.Children.Add(keyBlockContainer); // For normal keys + actionKey = key; } } + + foreach (var key in modifierKeys) + { + keyList.Add(GetKeyDisplayName((int)key)); + } + + if (actionKey.HasValue) + { + keyList.Add(GetKeyDisplayName((int)actionKey.Value)); + } + + return keyList; + } + + private string GetKeyDisplayName(int keyCode) + { + var keyName = new System.Text.StringBuilder(64); + KeyboardManagerInterop.GetKeyDisplayName(keyCode, keyName, keyName.Capacity); + return keyName.ToString(); + } + + private bool IsModifierKey(VirtualKey key) + { + return key == VirtualKey.Control + || key == VirtualKey.LeftControl + || key == VirtualKey.RightControl + || key == VirtualKey.Menu + || key == VirtualKey.LeftMenu + || key == VirtualKey.RightMenu + || key == VirtualKey.Shift + || key == VirtualKey.LeftShift + || key == VirtualKey.RightShift + || key == VirtualKey.LeftWindows + || key == VirtualKey.RightWindows; } public void SetRemappedKeys(List keys) { - RemappedKeys.ItemsSource = keys; + _remappedKeys.Clear(); + if (keys != null) + { + foreach (var key in keys) + { + _remappedKeys.Add(key); + } + } } public void SetOriginalKeys(List keys) { - OriginalKeys.ItemsSource = keys; + _originalKeys.Clear(); + if (keys != null) + { + foreach (var key in keys) + { + _originalKeys.Add(key); + } + } } public List GetOriginalKeys() { - return pressedKeys as List ?? new List(); + return _originalKeys.ToList(); } public List GetRemappedKeys() { - return newPressedKeys as List ?? new List(); + return _remappedKeys.ToList(); } public bool GetIsAppSpecific() @@ -170,12 +249,16 @@ namespace KeyboardManagerEditorUI.Styles { NewMode = true; RemappedToggleBtn.IsChecked = false; + + this.Focus(FocusState.Programmatic); } private void OriginalToggleBtn_Checked(object sender, RoutedEventArgs e) { NewMode = false; OriginalToggleBtn.IsChecked = false; + + this.Focus(FocusState.Programmatic); } public void SetApp(bool isSpecificApp, string appName) @@ -209,20 +292,22 @@ namespace KeyboardManagerEditorUI.Styles AllAppsCheckBox.Unchecked += AllAppsCheckBox_Unchecked; } - private string NormalizeKeyName(string keyName) + public void Dispose() { - switch (keyName) + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (!_disposed) { - case "Control": - return "Ctrl"; - case "Menu": - return "Alt"; - case "LeftWindows": - return "LWin"; - case "RightWindows": - return "RWin"; - default: - return keyName; + if (disposing) + { + CleanupKeyboardHook(); + } + + _disposed = true; } } }