Skip to content

Commit

Permalink
Hotkey picker
Browse files Browse the repository at this point in the history
  • Loading branch information
FrankvdStam committed Sep 9, 2024
1 parent 01d3280 commit de1d241
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 1 deletion.
4 changes: 3 additions & 1 deletion src/SoulSplitter/UI/EldenRing/EldenRingControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,10 @@

<!-- row ============================================================================================================================================================================================== -->

<generic:HotkeyPicker Grid.Row="{StaticResource StartAutomaticallyRow}"/>

<CheckBox
Grid.Row="{StaticResource StartAutomaticallyRow}"
Grid.Row="2"
Margin="5"
Content="Start automatically"
IsChecked="{Binding StartAutomatically}"/>
Expand Down
48 changes: 48 additions & 0 deletions src/SoulSplitter/UI/Generic/HotkeyPicker.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!-- This file is part of the SoulSplitter distribution (https://github.com/FrankvdStam/SoulSplitter).
Copyright (c) 2022 Frank van der Stam.
https://github.com/FrankvdStam/SoulSplitter/blob/main/LICENSE
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see<http://www.gnu.org/licenses/> . -->


<UserControl x:Class="SoulSplitter.UI.Generic.HotkeyPicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SoulSplitter.UI.Generic"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"

Check warning on line 24 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant namespace alias

Namespace alias 'materialDesign' is not required by code and can be safely removed

Check warning on line 24 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant namespace alias

Namespace alias 'materialDesign' is not required by code and can be safely removed
xmlns:validation="clr-namespace:SoulSplitter.UI.Validation"

Check warning on line 25 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant namespace alias

Namespace alias 'validation' is not required by code and can be safely removed

Check warning on line 25 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant namespace alias

Namespace alias 'validation' is not required by code and can be safely removed
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:BaseViewModel, IsDesignTimeCreatable=True}">

<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>

<!-- Material design -->
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/materialdesigncolor.indigo.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.indigo.xaml" />

</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>

<TextBox
IsReadOnly="True"
PreviewMouseDown="OnPreviewMouseDown"
PreviewKeyDown="OnPreviewKeyDown"
PreviewLostKeyboardFocus="OnPreviewLostKeyboardFocus"/>
</UserControl>
130 changes: 130 additions & 0 deletions src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// This file is part of the SoulSplitter distribution (https://github.com/FrankvdStam/SoulSplitter).
// Copyright (c) 2022 Frank van der Stam.
// https://github.com/FrankvdStam/SoulSplitter/blob/main/LICENSE
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

using System.Collections.Generic;
using System.Globalization;

Check warning on line 18 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed

Check warning on line 18 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using System.Windows.Forms;

Check warning on line 19 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed

Check warning on line 19 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using System.Windows.Input;
using SoulSplitter.UI.EldenRing;

Check warning on line 21 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed

Check warning on line 21 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant using directive

Using directive is not required by the code and can be safely removed
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
using TextBox = System.Windows.Controls.TextBox;
using UserControl = System.Windows.Controls.UserControl;

namespace SoulSplitter.UI.Generic
{
/// <summary>
/// Interaction logic for HotkeyPicker.xaml
/// </summary>
public partial class HotkeyPicker : UserControl

Check warning on line 31 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant class or interface specification in base types list

Base type 'UserControl' is already specified in other parts

Check warning on line 31 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant class or interface specification in base types list

Base type 'UserControl' is already specified in other parts
{
public HotkeyPicker()
{
InitializeComponent();
}

public class HotkeyCompletedParameter
{
public object Sender;
public ModifierKeys ModifierKeys;
public Key Key;
}

public RelayCommand HotkeyCompletedCommand { get; set; }

Check warning on line 45 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Auto-property accessor is never used (non-private accessibility)

Auto-property accessor 'HotkeyCompletedCommand.set' is never used

private bool _isActive = false;

Check warning on line 47 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant member initializer

Initializing field by default value is redundant

Check warning on line 47 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant member initializer

Initializing field by default value is redundant
private ModifierKeys _modifierKeys = ModifierKeys.None;
private Key _key = Key.None;
private void OnPreviewKeyDown(object sender, KeyEventArgs e)
{
if (_isActive)
{
if (e.IsDown && sender is TextBox textBox)
{
//Get modifier key and remember state
if (_modifierKeysLookup.TryGetValue(e.Key, out ModifierKeys modifierKeys))
{
if (!_modifierKeys.HasFlag(modifierKeys))
{
if (!string.IsNullOrWhiteSpace(textBox.Text))
{
textBox.Text += " + ";
}

textBox.Text += modifierKeys;
_modifierKeys |= modifierKeys;
}
}
else //get regular key, end listening for keypresses, invoke command
{
if (!string.IsNullOrWhiteSpace(textBox.Text))
{
textBox.Text += " + ";
}

textBox.Text += e.Key;
_key = e.Key;

HotkeyCompletedCommand?.Execute(new HotkeyCompletedParameter
{
Sender = this,
Key = _key,
ModifierKeys = _modifierKeys
});
_isActive = false;
}

var converter = new ModifierKeysConverter();

Check warning on line 89 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Unused local variable

Local variable 'converter' is never used

Check warning on line 89 in src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Unused local variable

Local variable 'converter' is never used
e.Handled = true;
}
}
}

private void OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (!_isActive && sender is TextBox textBox)
{
_isActive = true;
_key = Key.None;
_modifierKeys = ModifierKeys.None;
textBox.Text = "";
}
}

private static Dictionary<Key, ModifierKeys> _modifierKeysLookup = new Dictionary<Key, ModifierKeys>()
{
{ Key.LeftAlt, ModifierKeys.Alt },
{ Key.RightAlt, ModifierKeys.Alt },
{ Key.System, ModifierKeys.Alt },
{ Key.LeftCtrl, ModifierKeys.Control },
{ Key.RightCtrl, ModifierKeys.Control },
{ Key.LeftShift, ModifierKeys.Shift },
{ Key.RightShift, ModifierKeys.Shift },
{ Key.LWin, ModifierKeys.Windows },
{ Key.RWin, ModifierKeys.Windows },
};

private void OnPreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (_isActive && sender is TextBox textBox)
{
_isActive = false;
_key = Key.None;
_modifierKeys = ModifierKeys.None;
textBox.Text = "";
}
}
}
}
2 changes: 2 additions & 0 deletions src/cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ internal class Program
[STAThread]
static void Main(string[] args)
{
TestUi();

GlobalHotKey.RegisterHotKey(ModifierKeys.Alt, Key.A, () =>{ Debug.WriteLine("A"); });
GlobalHotKey.RegisterHotKey(ModifierKeys.Alt, Key.S, () =>{ Debug.WriteLine("S"); });
GlobalHotKey.RegisterHotKey(ModifierKeys.Alt, Key.D, () =>{ Debug.WriteLine("D"); });
Expand Down

0 comments on commit de1d241

Please sign in to comment.