Customisable shortcuts: first bulky implementation

This commit is contained in:
Théo Marchal 2022-04-05 00:09:40 +02:00
parent 9a5f686dde
commit 6d75f88927
8 changed files with 703 additions and 115 deletions

View File

@ -37,7 +37,7 @@ namespace unison
{
_systray.Dispose();
_snapcast.LaunchOrExit(true);
_hotkeys.RemoveHotKeys();
_hotkeys.RemoveHotkeys();
base.OnExit(e);
}
}

View File

@ -15,21 +15,168 @@ namespace unison
private const int HOTKEY_ID = 9000;
// modifiers
private const uint MOD_NONE = 0x0000;
private const uint MOD_ALT = 0x0001;
private const uint MOD_CONTROL = 0x0002;
private const uint MOD_SHIFT = 0x0004;
private const uint MOD_WIN = 0x0008;
public enum MOD : int
{
None = 0x0000,
Alt = 0x0001,
Control = 0x0002,
Shift = 0x0004,
//Win = 0x0008
};
// reference => https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
private const uint VK_MEDIA_PREV_TRACK = 0xB1;
private const uint VK_MEDIA_NEXT_TRACK = 0xB0;
private const uint VK_MEDIA_PLAY_PAUSE = 0xB3;
private const uint VK_VOLUME_UP = 0xAF;
private const uint VK_VOLUME_DOWN = 0xAE;
private const uint VK_VOLUME_MUTE = 0xAD;
private const uint VK_ENTER = 0x0D;
public enum VK : int
{
None = 0x00,
Back = 0x08,
Tab = 0x09,
Clear = 0x0c,
Return = 0x0d,
Menu = 0x12,
Pause = 0x13,
Capital = 0x14,
Kana = 0x15,
Hangul = 0x15,
Junja = 0x17,
Final = 0x18,
Hanja = 0x19,
Kanji = 0x19,
Escape = 0x1b,
Convert = 0x1c,
NonConvert = 0x1d,
Accept = 0x1e,
ModeChange = 0x1f,
Space = 0x20,
Prior = 0x21,
Next = 0x22,
End = 0x23,
Home = 0x24,
Left = 0x25,
Up = 0x26,
Right = 0x27,
Down = 0x28,
Select = 0x29,
Print = 0x2a,
Execute = 0x2b,
Snapshot = 0x2c,
Insert = 0x2d,
Delete = 0x2e,
Help = 0x2f,
NumRow0 = 0x30,
NumRow1 = 0x31,
NumRow2 = 0x32,
NumRow3 = 0x33,
NumRow4 = 0x34,
NumRow5 = 0x35,
NumRow6 = 0x36,
NumRow7 = 0x37,
NumRow8 = 0x38,
NumRow9 = 0x39,
A = 0x41,
B = 0x42,
C = 0x43,
D = 0x44,
E = 0x45,
F = 0x46,
G = 0x47,
H = 0x48,
I = 0x49,
J = 0x4a,
K = 0x4b,
L = 0x4c,
M = 0x4d,
N = 0x4e,
O = 0x4f,
P = 0x50,
Q = 0x51,
R = 0x52,
S = 0x53,
T = 0x54,
U = 0x55,
V = 0x56,
W = 0x57,
X = 0x58,
Y = 0x59,
Z = 0x5a,
Apps = 0x5d,
Sleep = 0x5f,
NumPad0 = 0x60,
NumPad1 = 0x61,
NumPad2 = 0x62,
NumPad3 = 0x63,
NumPad4 = 0x64,
NumPad5 = 0x65,
NumPad6 = 0x66,
NumPad7 = 0x67,
NumPad8 = 0x68,
NumPad9 = 0x69,
Multiply = 0x6a,
Add = 0x6b,
Separator = 0x6c,
Subtract = 0x6d,
Decimal = 0x6e,
Divide = 0x6f,
F1 = 0x70,
F2 = 0x71,
F3 = 0x72,
F4 = 0x73,
F5 = 0x74,
F6 = 0x75,
F7 = 0x76,
F8 = 0x77,
F9 = 0x78,
F10 = 0x79,
F11 = 0x7a,
F12 = 0x7b,
F13 = 0x7c,
F14 = 0x7d,
F15 = 0x7e,
F16 = 0x7f,
F17 = 0x80,
F18 = 0x81,
F19 = 0x82,
F20 = 0x83,
F21 = 0x84,
F22 = 0x85,
F23 = 0x86,
F24 = 0x87,
NumLock = 0x90,
Scroll = 0x91,
LMenu = 0xa4,
RMenu = 0xa5,
BrowserBack = 0xa6,
BrowserForward = 0xa7,
BrowserRefresh = 0xa8,
BrowserStop = 0xa9,
BrowserSearch = 0xaa,
BrowserFavorites = 0xab,
BrowserHome = 0xac,
VolumeMute = 0xad,
VolumeDown = 0xae,
VolumeUp = 0xaf,
MediaNextTrack = 0xb0,
MediaPreviousTrack = 0xb1,
MediaStop = 0xb2,
MediaPlayPause = 0xb3,
LaunchMail = 0xb4,
LaunchMediaSelect = 0xb5,
LaunchApp1 = 0xb6,
LaunchApp2 = 0xb7,
};
public struct HotkeyPair
{
public MOD mod;
public VK vk;
public HotkeyPair(MOD _mod, VK _vk) { mod = _mod; vk = _vk; }
public uint GetMOD() { return (uint)mod; }
public uint GetVK() { return (uint)vk; }
public void SetMOD(MOD modmod) { mod = modmod; }
public void SetVK(VK vkvk) { vk = vkvk; }
}
private MainWindow _appWindow;
private readonly MPDHandler _mpd;
@ -37,9 +184,26 @@ namespace unison
private IntPtr _windowHandle;
private HwndSource _source;
public HotkeyPair _NextTrack;
public HotkeyPair _PreviousTrack;
public HotkeyPair _PlayPause;
public HotkeyPair _VolumeUp;
public HotkeyPair _VolumeDown;
public HotkeyPair _VolumeMute;
public HotkeyPair _ShowWindow;
public HotkeyHandler()
{
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
// get shortcuts from settings
_NextTrack = new HotkeyPair((MOD)Properties.Settings.Default.nextTrack_mod, (VK)Properties.Settings.Default.nextTrack_vk);
_PreviousTrack = new HotkeyPair((MOD)Properties.Settings.Default.previousTrack_mod, (VK)Properties.Settings.Default.previousTrack_vk);
_PlayPause = new HotkeyPair((MOD)Properties.Settings.Default.playPause_mod, (VK)Properties.Settings.Default.playPause_vk);
_VolumeUp = new HotkeyPair((MOD)Properties.Settings.Default.volumeUp_mod, (VK)Properties.Settings.Default.volumeUp_vk);
_VolumeDown = new HotkeyPair((MOD)Properties.Settings.Default.volumeDown_mod, (VK)Properties.Settings.Default.volumeDown_vk);
_VolumeMute = new HotkeyPair((MOD)Properties.Settings.Default.volumeMute_mod, (VK)Properties.Settings.Default.volumeMute_vk);
_ShowWindow = new HotkeyPair((MOD)Properties.Settings.Default.showWindow_mod, (VK)Properties.Settings.Default.showWindow_vk);
}
public void Activate(Window win)
@ -48,18 +212,28 @@ namespace unison
{
_windowHandle = new WindowInteropHelper(win).Handle;
_source = HwndSource.FromHwnd(_windowHandle);
_source.AddHook(HwndHook);
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_MEDIA_PREV_TRACK);
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_MEDIA_NEXT_TRACK);
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_MEDIA_PLAY_PAUSE);
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_VOLUME_UP);
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_VOLUME_DOWN);
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_VOLUME_MUTE);
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL | MOD_ALT, VK_ENTER);
AddHotkeys();
}
}
public void AddHotkeys()
{
_source.AddHook(HwndHook);
RegisterHotKey(_windowHandle, HOTKEY_ID, _NextTrack.GetMOD(), _NextTrack.GetVK());
RegisterHotKey(_windowHandle, HOTKEY_ID, _PreviousTrack.GetMOD(), _PreviousTrack.GetVK());
RegisterHotKey(_windowHandle, HOTKEY_ID, _PlayPause.GetMOD(), _PlayPause.GetVK());
RegisterHotKey(_windowHandle, HOTKEY_ID, _VolumeUp.GetMOD(), _VolumeUp.GetVK());
RegisterHotKey(_windowHandle, HOTKEY_ID, _VolumeDown.GetMOD(), _VolumeDown.GetVK());
RegisterHotKey(_windowHandle, HOTKEY_ID, _VolumeMute.GetMOD(), _VolumeMute.GetVK());
RegisterHotKey(_windowHandle, HOTKEY_ID, _ShowWindow.GetMOD(), _ShowWindow.GetVK());
}
public void RemoveHotkeys()
{
_source.RemoveHook(HwndHook);
UnregisterHotKey(_windowHandle, HOTKEY_ID);
}
private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
const int WM_HOTKEY = 0x0312;
@ -67,61 +241,48 @@ namespace unison
if (msg == WM_HOTKEY && wParam.ToInt32() == HOTKEY_ID)
{
uint vkey = ((uint)lParam >> 16) & 0xFFFF;
switch (vkey)
{
case VK_MEDIA_NEXT_TRACK:
_mpd.Next();
break;
case VK_MEDIA_PREV_TRACK:
_mpd.Prev();
break;
case VK_VOLUME_DOWN:
_mpd.VolumeDown();
break;
case VK_VOLUME_UP:
_mpd.VolumeUp();
break;
case VK_VOLUME_MUTE:
_mpd.VolumeMute();
break;
case VK_MEDIA_PLAY_PAUSE:
_mpd.PlayPause();
break;
case VK_ENTER:
if (_appWindow == null)
_appWindow = (MainWindow)Application.Current.MainWindow;
if (_appWindow.WindowState == WindowState.Minimized)
if (vkey == _NextTrack.GetVK())
_mpd.Next();
else if (vkey == _PreviousTrack.GetVK())
_mpd.Prev();
else if (vkey == _PlayPause.GetVK())
_mpd.PlayPause();
else if (vkey == _VolumeUp.GetVK())
_mpd.VolumeUp();
else if (vkey == _VolumeDown.GetVK())
_mpd.VolumeDown();
else if (vkey == _VolumeMute.GetVK())
_mpd.VolumeMute();
else if (vkey == _ShowWindow.GetVK())
{
if (_appWindow == null)
_appWindow = (MainWindow)Application.Current.MainWindow;
if (_appWindow.WindowState == WindowState.Minimized)
{
_appWindow.Show();
_appWindow.Activate();
_appWindow.WindowState = WindowState.Normal;
}
else
{
if (_appWindow.IsActive)
{
_appWindow.Hide();
_appWindow.WindowState = WindowState.Minimized;
}
else // not minimized but not in front
{
_appWindow.Show();
_appWindow.Activate();
_appWindow.WindowState = WindowState.Normal;
}
else
{
if (_appWindow.IsActive)
{
_appWindow.Hide();
_appWindow.WindowState = WindowState.Minimized;
}
else // not minimized but not in front
{
_appWindow.Show();
_appWindow.Activate();
_appWindow.WindowState = WindowState.Normal;
}
}
break;
}
}
handled = true;
}
return IntPtr.Zero;
}
public void RemoveHotKeys()
{
_source.RemoveHook(HwndHook);
UnregisterHotKey(_windowHandle, HOTKEY_ID);
}
}
}

View File

@ -118,5 +118,173 @@ namespace unison.Properties {
this["volume_offset"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("2")]
public uint nextTrack_mod {
get {
return ((uint)(this["nextTrack_mod"]));
}
set {
this["nextTrack_mod"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("176")]
public uint nextTrack_vk {
get {
return ((uint)(this["nextTrack_vk"]));
}
set {
this["nextTrack_vk"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("2")]
public uint previousTrack_mod {
get {
return ((uint)(this["previousTrack_mod"]));
}
set {
this["previousTrack_mod"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("177")]
public uint previousTrack_vk {
get {
return ((uint)(this["previousTrack_vk"]));
}
set {
this["previousTrack_vk"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("2")]
public uint playPause_mod {
get {
return ((uint)(this["playPause_mod"]));
}
set {
this["playPause_mod"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("179")]
public uint playPause_vk {
get {
return ((uint)(this["playPause_vk"]));
}
set {
this["playPause_vk"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("2")]
public uint volumeUp_mod {
get {
return ((uint)(this["volumeUp_mod"]));
}
set {
this["volumeUp_mod"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("175")]
public uint volumeUp_vk {
get {
return ((uint)(this["volumeUp_vk"]));
}
set {
this["volumeUp_vk"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("2")]
public uint volumeDown_mod {
get {
return ((uint)(this["volumeDown_mod"]));
}
set {
this["volumeDown_mod"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("174")]
public uint volumeDown_vk {
get {
return ((uint)(this["volumeDown_vk"]));
}
set {
this["volumeDown_vk"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("2")]
public uint volumeMute_mod {
get {
return ((uint)(this["volumeMute_mod"]));
}
set {
this["volumeMute_mod"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("173")]
public uint volumeMute_vk {
get {
return ((uint)(this["volumeMute_vk"]));
}
set {
this["volumeMute_vk"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("3")]
public uint showWindow_mod {
get {
return ((uint)(this["showWindow_mod"]));
}
set {
this["showWindow_mod"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("13")]
public uint showWindow_vk {
get {
return ((uint)(this["showWindow_vk"]));
}
set {
this["showWindow_vk"] = value;
}
}
}
}

View File

@ -26,5 +26,47 @@
<Setting Name="volume_offset" Type="System.Int32" Scope="User">
<Value Profile="(Default)">5</Value>
</Setting>
<Setting Name="nextTrack_mod" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">2</Value>
</Setting>
<Setting Name="nextTrack_vk" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">176</Value>
</Setting>
<Setting Name="previousTrack_mod" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">2</Value>
</Setting>
<Setting Name="previousTrack_vk" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">177</Value>
</Setting>
<Setting Name="playPause_mod" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">2</Value>
</Setting>
<Setting Name="playPause_vk" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">179</Value>
</Setting>
<Setting Name="volumeUp_mod" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">2</Value>
</Setting>
<Setting Name="volumeUp_vk" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">175</Value>
</Setting>
<Setting Name="volumeDown_mod" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">2</Value>
</Setting>
<Setting Name="volumeDown_vk" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">174</Value>
</Setting>
<Setting Name="volumeMute_mod" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">2</Value>
</Setting>
<Setting Name="volumeMute_vk" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">173</Value>
</Setting>
<Setting Name="showWindow_mod" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">3</Value>
</Setting>
<Setting Name="showWindow_vk" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">13</Value>
</Setting>
</Settings>
</SettingsFile>

View File

@ -37,13 +37,12 @@ Through [Radio-Browser](https://www.radio-browser.info), a community database, y
### Missing features
* Custom shortcuts.
* MPD passwords: I don't really see the point, but if asked, I will integrate them.
* Any sort of playlist and queue management. I use other software to do it, but I might implement them at some point.
### Wanted features
* A complete shuffle system based on set criteria, aka a smart playlist.
* Any sort of playlist and queue management. I use other software to do it, but I will implement them at some point.
## Translations

View File

@ -109,11 +109,12 @@
</GroupBox.Header>
<Grid>
<StackPanel>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeOffset}" TextWrapping="Wrap" Margin="0,2,0,0"/>
<TextBox x:Name="VolumeOffset" TextWrapping="Wrap" Width="25" PreviewTextInput="NumberValidationTextBox" Margin="8,2,0,0"/>
</StackPanel>
<Grid MinWidth="300" Margin="0,5,0,0">
<Grid MinWidth="420" Margin="0,5,0,0" x:Name="RebindKeyWrapper">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
@ -126,23 +127,79 @@
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{x:Static properties:Resources.Settings_NextTrack}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="0" Margin="1"/>
<TextBlock Text="{x:Static properties:Resources.Settings_PreviousTrack}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="1" Margin="1"/>
<TextBlock Text="{x:Static properties:Resources.Settings_PlayPause}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="2" Margin="1"/>
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeUp}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="3" Margin="1"/>
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeDown}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="4" Margin="1"/>
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeMute}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="5" Margin="1"/>
<TextBlock Text="{x:Static properties:Resources.Settings_ShowWindow}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="6" Margin="1"/>
<TextBlock Text="{x:Static properties:Resources.Settings_NextTrack}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="0" Margin="1,1,1,1" Grid.RowSpan="2"/>
<TextBlock Text="{x:Static properties:Resources.Settings_PreviousTrack}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="2" Margin="1,1,1,1"/>
<TextBlock Text="{x:Static properties:Resources.Settings_PlayPause}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="3" Margin="1,1,1,1"/>
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeUp}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="4" Margin="1,1,1,1"/>
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeDown}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="5" Margin="1,1,1,1"/>
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeMute}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="6" Margin="1,1,1,1"/>
<TextBlock Text="{x:Static properties:Resources.Settings_ShowWindow}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="7" Margin="1,1,1,1"/>
<TextBlock Text="ctrl + media_next" TextWrapping="Wrap" Grid.Column="1" Grid.Row="0" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
<TextBlock Text="ctrl + media_prev" TextWrapping="Wrap" Grid.Column="1" Grid.Row="1" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
<TextBlock Text="ctrl + media_play" TextWrapping="Wrap" Grid.Column="1" Grid.Row="2" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
<TextBlock Text="ctrl + volume_up" TextWrapping="Wrap" Grid.Column="1" Grid.Row="3" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
<TextBlock Text="ctrl + volume_down" TextWrapping="Wrap" Grid.Column="1" Grid.Row="4" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
<TextBlock Text="ctrl + volume_mute" TextWrapping="Wrap" Grid.Column="1" Grid.Row="5" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
<TextBlock Text="ctrl + alt + enter" TextWrapping="Wrap" Grid.Column="1" Grid.Row="6" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
<StackPanel x:Name="Shortcut_NextTrack" Orientation="Horizontal" Grid.Column="1" Grid.Row="0" Margin="0,0,0,2" Grid.RowSpan="2">
<ComboBox Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Bold"></ComboBox>
<ComboBox Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Bold"/>
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="[key]" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Bold" VerticalAlignment="Stretch"/>
</Button>
</StackPanel>
<StackPanel x:Name="Shortcut_PreviousTrack" Orientation="Horizontal" Grid.Column="1" Grid.Row="2" Margin="0,0,0,2">
<ComboBox Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Bold"></ComboBox>
<ComboBox Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Bold"></ComboBox>
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="[key]" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Bold" VerticalAlignment="Stretch"/>
</Button>
</StackPanel>
<StackPanel x:Name="Shortcut_PlayPause" Orientation="Horizontal" Grid.Column="1" Grid.Row="3" Margin="0,0,0,2">
<ComboBox Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Bold"></ComboBox>
<ComboBox Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Bold"></ComboBox>
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="[key]" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Bold" VerticalAlignment="Stretch"/>
</Button>
</StackPanel>
<StackPanel x:Name="Shortcut_VolumeUp" Orientation="Horizontal" Grid.Column="1" Grid.Row="4" Margin="0,0,0,2">
<ComboBox Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Bold"></ComboBox>
<ComboBox Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Bold"></ComboBox>
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="[key]" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Bold" VerticalAlignment="Stretch"/>
</Button>
</StackPanel>
<StackPanel x:Name="Shortcut_VolumeDown" Orientation="Horizontal" Grid.Column="1" Grid.Row="5" Margin="0,0,0,2">
<ComboBox Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Bold"></ComboBox>
<ComboBox Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Bold"></ComboBox>
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="[key]" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Bold" VerticalAlignment="Stretch"/>
</Button>
</StackPanel>
<StackPanel x:Name="Shortcut_VolumeMute" Orientation="Horizontal" Grid.Column="1" Grid.Row="6" Margin="0,0,0,2">
<ComboBox Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Bold"></ComboBox>
<ComboBox Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Bold"></ComboBox>
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="[key]" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Bold" VerticalAlignment="Stretch"/>
</Button>
</StackPanel>
<StackPanel x:Name="Shortcut_ShowWindow" Orientation="Horizontal" Grid.Column="1" Grid.Row="7">
<ComboBox Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Bold"></ComboBox>
<ComboBox Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Bold"></ComboBox>
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="[key]" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Bold" VerticalAlignment="Stretch"/>
</Button>
</StackPanel>
</Grid>
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<TextBlock Text="Please note that if the input key is not recognized, this is due to a limitation on how virtual keys work." TextWrapping="Wrap" Margin="0,2,0,0" MaxWidth="420" />
</StackPanel>
<Button Content="{x:Static properties:Resources.Settings_SnapcastResetButton}" Margin="0,5,0,0" Width="120" Click="ShortcutsReset_Clicked"/>
</StackPanel>
</Grid>
</GroupBox>

View File

@ -2,9 +2,11 @@
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Navigation;
@ -13,6 +15,8 @@ namespace unison
{
public partial class Settings : Window
{
Key _pressedKey = Key.None;
public static string GetVersion => Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
public static string GetLicense
@ -49,6 +53,52 @@ namespace unison
SnapcastPath.Text = Properties.Settings.Default.snapcast_path;
SnapcastPort.Text = Properties.Settings.Default.snapcast_port.ToString();
VolumeOffset.Text = Properties.Settings.Default.volume_offset.ToString();
SetupShortcuts();
}
private void SetupShortcuts()
{
var MODValues = System.Enum.GetValues(typeof(HotkeyHandler.MOD));
System.Collections.Generic.IEnumerable<StackPanel> stackPanelCollection = RebindKeyWrapper.Children.OfType<StackPanel>();
foreach (StackPanel stackPanel in stackPanelCollection)
{
if (stackPanel.Name.Contains("Shortcut"))
{
HotkeyHandler.HotkeyPair hotkey = GetHotkeyVariable(stackPanel.Name);
System.Collections.Generic.IEnumerable<ComboBox> comboBoxCollection = stackPanel.Children.OfType<ComboBox>();
HotkeyHandler.MOD[] MODList = System.Enum.GetValues(typeof(HotkeyHandler.MOD))
.OfType<HotkeyHandler.MOD>()
.Select(x => x & (HotkeyHandler.MOD)hotkey.GetMOD())
.Where(x => x != HotkeyHandler.MOD.None)
.ToArray();
foreach (ComboBox comboBox in comboBoxCollection)
{
foreach (var value in MODValues)
{
comboBox.Items.Add(value.ToString().ToLower());
comboBox.SelectedItem = comboBox.Items[0];
comboBox.FontWeight = FontWeights.Light;
}
}
for (int i = 0; i < comboBoxCollection.ToArray().Length; i++)
{
if (i < MODList.Length)
{
comboBoxCollection.ToArray()[i].SelectedItem = MODList[i].ToString().ToLower();
comboBoxCollection.ToArray()[i].FontWeight = FontWeights.Bold;
}
}
System.Collections.Generic.IEnumerable<Button> buttonCollection = stackPanel.Children.OfType<Button>();
TextBlock textblock = (TextBlock)buttonCollection.First().Content;
textblock.Text = ((HotkeyHandler.VK)hotkey.GetVK()).ToString();
}
}
}
private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
@ -88,6 +138,126 @@ namespace unison
SnapcastPort.Text = (string)Application.Current.FindResource("snapcastPort");
}
private void HotkeyChanged()
{
HotkeyHandler hotkeys = (HotkeyHandler)Application.Current.Properties["hotkeys"];
hotkeys.RemoveHotkeys();
hotkeys.AddHotkeys();
}
private ref HotkeyHandler.HotkeyPair GetHotkeyVariable(string Name)
{
HotkeyHandler hotkeys = (HotkeyHandler)Application.Current.Properties["hotkeys"];
if (Name == "Shortcut_NextTrack")
return ref hotkeys._NextTrack;
if (Name == "Shortcut_PreviousTrack")
return ref hotkeys._PreviousTrack;
if (Name == "Shortcut_PlayPause")
return ref hotkeys._PlayPause;
if (Name == "Shortcut_VolumeUp")
return ref hotkeys._VolumeUp;
if (Name == "Shortcut_VolumeDown")
return ref hotkeys._VolumeDown;
if (Name == "Shortcut_VolumeMute")
return ref hotkeys._VolumeMute;
if (Name == "Shortcut_ShowWindow")
return ref hotkeys._ShowWindow;
return ref hotkeys._NextTrack;
}
private void UpdateHotkey_MOD(string Name, HotkeyHandler.MOD mod) => GetHotkeyVariable(Name).SetMOD(mod);
private void UpdateHotkey_VK(string Name, HotkeyHandler.VK vk) => GetHotkeyVariable(Name).SetVK(vk);
private void MOD_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!IsLoaded)
return;
ComboBox combobox = (ComboBox)sender;
StackPanel stackpanel = (StackPanel)combobox.Parent;
System.Collections.Generic.IEnumerable<ComboBox> collection = stackpanel.Children.OfType<ComboBox>();
HotkeyHandler.MOD MOD1, MOD2;
// we need to do this because the element is modified -after- this function
if (combobox.Tag.ToString() == "MOD1")
{
MOD1 = GetMOD(e.AddedItems[0].ToString());
MOD2 = GetMOD(collection.Last().Text);
}
else
{
MOD1 = GetMOD(collection.First().Text);
MOD2 = GetMOD(e.AddedItems[0].ToString());
}
if (e.AddedItems[0].ToString() == "none")
combobox.FontWeight = FontWeights.Light;
else
combobox.FontWeight = FontWeights.Bold;
HotkeyHandler.MOD ModKey = MOD1 | MOD2;
UpdateHotkey_MOD(stackpanel.Name, ModKey);
HotkeyChanged();
}
private void RemapKey_Clicked(object sender, RoutedEventArgs e)
{
Button button = (Button)sender;
TextBlock textBlock = (TextBlock)button.Content;
textBlock.Text = "Enter key...";
button.PreviewKeyDown += DetectPressedKey;
}
private void DetectPressedKey(object sender, KeyEventArgs e)
{
e.Handled = true; // not enough to handle media keys triggering in this setup
_pressedKey = e.Key;
HotkeyHandler.VK VirtualKey = GetVirtualKey(_pressedKey);
if (VirtualKey == HotkeyHandler.VK.None)
_pressedKey = Key.None;
Button button = (Button)sender;
TextBlock textBlock = (TextBlock)button.Content;
StackPanel stackPanel = (StackPanel)button.Parent;
textBlock.Text = _pressedKey.ToString();
button.PreviewKeyDown -= DetectPressedKey;
UpdateHotkey_VK(stackPanel.Name, VirtualKey);
HotkeyChanged();
}
private HotkeyHandler.VK GetVirtualKey(Key key)
{
var values = System.Enum.GetValues(typeof(HotkeyHandler.VK));
foreach (object value in values)
{
if (key.ToString().ToLower() == value.ToString().ToLower())
return (HotkeyHandler.VK)value;
}
return HotkeyHandler.VK.None;
}
private HotkeyHandler.MOD GetMOD(string str)
{
var values = System.Enum.GetValues(typeof(HotkeyHandler.MOD));
foreach (object value in values)
{
if (str.ToLower() == value.ToString().ToLower())
return (HotkeyHandler.MOD)value;
}
return HotkeyHandler.MOD.None;
}
private void ShortcutsReset_Clicked(object sender, RoutedEventArgs e)
{
// todo
}
public void UpdateStats()
{
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
@ -100,6 +270,8 @@ namespace unison
StatDatabaseUpdate.Text = mpd.GetStats().DatabaseUpdate.ToString();
}
public void SaveSettings()
{
Properties.Settings.Default.mpd_host = MpdHost.Text;
@ -110,6 +282,23 @@ namespace unison
Properties.Settings.Default.snapcast_path = SnapcastPath.Text;
Properties.Settings.Default.snapcast_port = int.Parse(SnapcastPort.Text, CultureInfo.InvariantCulture);
Properties.Settings.Default.volume_offset = int.Parse(VolumeOffset.Text, CultureInfo.InvariantCulture);
// todo
Properties.Settings.Default.nextTrack_mod = (uint)(GetMOD(Shortcut_NextTrack.Children.OfType<ComboBox>().First().SelectedItem.ToString()) | GetMOD(Shortcut_NextTrack.Children.OfType<ComboBox>().Last().SelectedItem.ToString()));
/*Properties.Settings.Default.nextTrack_vk) = (uint)GetVirtualKey(Shortcut_NextTrack.Children.OfType<Button>().First().)
Properties.Settings.Default.previousTrack_mod;
Properties.Settings.Default.previousTrack_vk);
Properties.Settings.Default.playPause_mod;
Properties.Settings.Default.playPause_vk);
Properties.Settings.Default.volumeUp_mod;
Properties.Settings.Default.volumeUp_vk);
Properties.Settings.Default.volumeDown_mod;
Properties.Settings.Default.volumeDown_vk);
Properties.Settings.Default.volumeMute_mod;
Properties.Settings.Default.volumeMute_vk);
Properties.Settings.Default.showWindow_mod;
Properties.Settings.Default.showWindow_vk);*/
Properties.Settings.Default.Save();
}

View File

@ -7,7 +7,7 @@
<ApplicationIcon>Resources\icon-full.ico</ApplicationIcon>
<Win32Resource></Win32Resource>
<StartupObject>unison.App</StartupObject>
<Version>1.1</Version>
<Version>1.2</Version>
<Company />
<Authors>Théo Marchal</Authors>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
@ -22,13 +22,6 @@
<None Remove="Resources\icon-mini.ico" />
<None Remove="Resources\nocover.png" />
<None Remove="LICENSE" />
<None Remove="snapclient_0.25.0-1_win64\FLAC.dll" />
<None Remove="snapclient_0.25.0-1_win64\ogg.dll" />
<None Remove="snapclient_0.25.0-1_win64\opus.dll" />
<None Remove="snapclient_0.25.0-1_win64\README.txt" />
<None Remove="snapclient_0.25.0-1_win64\snapclient.exe" />
<None Remove="snapclient_0.25.0-1_win64\soxr.dll" />
<None Remove="snapclient_0.25.0-1_win64\vorbis.dll" />
<None Include="LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
@ -48,27 +41,6 @@
<Content Include="LICENSE">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="snapclient_0.25.0-1_win64\README.txt">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Include="snapclient_0.25.0-1_win64\FLAC.dll">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Include="snapclient_0.25.0-1_win64\ogg.dll">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Include="snapclient_0.25.0-1_win64\opus.dll">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Include="snapclient_0.25.0-1_win64\snapclient.exe">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Include="snapclient_0.25.0-1_win64\soxr.dll">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Include="snapclient_0.25.0-1_win64\vorbis.dll">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>