Skip to content

Commit

Permalink
Added Global hotkey + bumped version
Browse files Browse the repository at this point in the history
  • Loading branch information
voltura committed Oct 17, 2021
1 parent 5a66073 commit eeacc44
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 15 deletions.
148 changes: 148 additions & 0 deletions GlobalHotkey.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Input;

namespace HardTop
{
internal class GlobalHotKey : IDisposable
{
/// <summary>
/// Registers a global hotkey
/// </summary>
/// <param name="aKeyGestureString">e.g. Alt + Shift + Control + Win + S</param>
/// <param name="aAction">Action to be called when hotkey is pressed</param>
/// <returns>true, if registration succeeded, otherwise false</returns>
public static bool RegisterHotKey(string aKeyGestureString, Action aAction)
{
var c = new KeyGestureConverter();
KeyGesture aKeyGesture = (KeyGesture)c.ConvertFrom(aKeyGestureString);
return RegisterHotKey(aKeyGesture.Modifiers, aKeyGesture.Key, aAction);
}

public static bool RegisterHotKey(ModifierKeys aModifier, Key aKey, Action aAction)
{
if (aModifier == ModifierKeys.None)
{
throw new ArgumentException("Modifier must not be ModifierKeys.None");
}
if (aAction is null)
{
throw new ArgumentNullException(nameof(aAction));
}

System.Windows.Forms.Keys aVirtualKeyCode = (System.Windows.Forms.Keys)KeyInterop.VirtualKeyFromKey(aKey);
currentID++;
bool aRegistered = RegisterHotKey(window.Handle, currentID,
(uint)aModifier | MOD_NOREPEAT,
(uint)aVirtualKeyCode);

if (aRegistered)
{
registeredHotKeys.Add(new HotKeyWithAction(aModifier, aKey, aAction));
}
return aRegistered;
}

public void Dispose()
{
// unregister all the registered hot keys.
for (int i = currentID; i > 0; i--) UnregisterHotKey(window.Handle, i);

// dispose the inner native window.
window.Dispose();
}

static GlobalHotKey()
{
window.KeyPressed += (s, e) =>
{
registeredHotKeys.ForEach(x =>
{
if (e.Modifier == x.Modifier && e.Key == x.Key) x.Action();
});
};
}

private static readonly InvisibleWindowForMessages window = new InvisibleWindowForMessages();
private static int currentID;
private static readonly uint MOD_NOREPEAT = 0x4000;
private static readonly List<HotKeyWithAction> registeredHotKeys = new List<HotKeyWithAction>();

private class HotKeyWithAction
{

public HotKeyWithAction(ModifierKeys modifier, Key key, Action action)
{
Modifier = modifier;
Key = key;
Action = action;
}

public ModifierKeys Modifier { get; }
public Key Key { get; }
public Action Action { get; }
}

// Registers a hot key with Windows.
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
// Unregisters the hot key with Windows.
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

private class InvisibleWindowForMessages : System.Windows.Forms.NativeWindow, IDisposable
{
public InvisibleWindowForMessages()
{
CreateHandle(new System.Windows.Forms.CreateParams());
}

private static readonly int WM_HOTKEY = 0x0312;
protected override void WndProc(ref System.Windows.Forms.Message m)
{
base.WndProc(ref m);

if (m.Msg == WM_HOTKEY)
{
var aWPFKey = KeyInterop.KeyFromVirtualKey(((int)m.LParam >> 16) & 0xFFFF);
ModifierKeys modifier = (ModifierKeys)((int)m.LParam & 0xFFFF);
KeyPressed?.Invoke(this, new HotKeyPressedEventArgs(modifier, aWPFKey));
}
}

public class HotKeyPressedEventArgs : EventArgs
{
private readonly ModifierKeys _modifier;
private readonly Key _key;

internal HotKeyPressedEventArgs(ModifierKeys modifier, Key key)
{
_modifier = modifier;
_key = key;
}

public ModifierKeys Modifier
{
get { return _modifier; }
}

public Key Key
{
get { return _key; }
}
}

public event EventHandler<HotKeyPressedEventArgs> KeyPressed;

#region IDisposable Members

public void Dispose()
{
this.DestroyHandle();
}

#endregion
}
}
}
3 changes: 3 additions & 0 deletions HardTop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
<ApplicationManifest>Properties\settings.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<Compile Include="GlobalHotkey.cs" />
<Compile Include="HardTopApplicationContext.cs" />
<Compile Include="Message.cs" />
<Compile Include="NativeMethods.cs" />
Expand Down Expand Up @@ -131,13 +132,15 @@
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="PresentationCore" />
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.8">
Expand Down
73 changes: 60 additions & 13 deletions HardTopContextMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Forms;

#endregion Using statements
Expand All @@ -11,10 +12,16 @@ namespace HardTop
{
internal class HardTopContextMenu : IDisposable
{
#region Private variables

private InvisibleWindowForContextMenu _invisibleWindow;

#endregion Private variables

#region Internal context menu

internal ContextMenu ContextMenu { get; private set; }
internal const int NUMBER_OF_FIXED_ITEMS = 9;
internal const int NUMBER_OF_FIXED_ITEMS = 13;

#endregion Internal context menu

Expand All @@ -26,6 +33,14 @@ internal HardTopContextMenu()
ContextMenu.Collapse += ContextMenu_Collapse;
ContextMenu.Popup += ContextMenu_Popup;
AddUpdatedWindowsInfoToContextMenu();
CreateInvisibleWindowForContextMenu();
GlobalHotKey.RegisterHotKey("Ctrl + Alt + Z", () => ShowWindowList());
}

private void CreateInvisibleWindowForContextMenu()
{
_invisibleWindow = new InvisibleWindowForContextMenu(ContextMenu);

}

#endregion Internal constructor
Expand Down Expand Up @@ -64,7 +79,7 @@ private void WindowItem_Click(object o, EventArgs e)

private static void ExitMenuClick(object o, EventArgs e)
{
Application.Exit();
if (MessageBox.Show($"Close {Application.ProductName}?", $"{Application.ProductName} {Resources.Version} {Application.ProductVersion}", MessageBoxButtons.YesNo) == DialogResult.Yes) Application.Exit();
}

private void StartWithWindowsClick(object o, EventArgs e)
Expand Down Expand Up @@ -95,18 +110,24 @@ private void AboutClick(object o, EventArgs e)

#region Private method for context menu creation

private void CreateContextMenu() => ContextMenu = new ContextMenu(new MenuItem[NUMBER_OF_FIXED_ITEMS]
private void CreateContextMenu()
{
new MenuItem(Resources.AboutMenu, AboutClick),
new MenuItem(Resources.SeparatorMenu),
new MenuItem(Resources.DonationMenu, (o, e) => { new Process() { StartInfo = new ProcessStartInfo(Resources.DonationUrl) { UseShellExecute = true } }.Start(); } ),
new MenuItem(Resources.SeparatorMenu),
new MenuItem(Resources.StartWithWindowsMenu, StartWithWindowsClick) { Checked = Settings.StartWithWindows },
new MenuItem(Resources.SeparatorMenu),
new MenuItem(Resources.ExitMenu, ExitMenuClick),
new MenuItem(Resources.WindowsMenu) { DefaultItem = true, BarBreak = true },
new MenuItem(Resources.SeparatorMenu)
});
ContextMenu = new ContextMenu(new MenuItem[NUMBER_OF_FIXED_ITEMS] {
new MenuItem(Resources.AboutMenu, AboutClick),
new MenuItem(Resources.SeparatorMenu),
new MenuItem(Resources.DonationMenu, (o, e) => { new Process() { StartInfo = new ProcessStartInfo(Resources.DonationUrl) { UseShellExecute = true } }.Start(); } ),
new MenuItem(Resources.SeparatorMenu),
new MenuItem(Resources.StartWithWindowsMenu, StartWithWindowsClick) { Checked = Settings.StartWithWindows },
new MenuItem(Resources.SeparatorMenu),
new MenuItem(Resources.CloseMenuMenu, (o, e) => { ; }),
new MenuItem(Resources.SeparatorMenu),
new MenuItem(Resources.ShowMenuMenu + " (" + Settings.GetSetting("GlobalHotkey") + ")"),
new MenuItem(Resources.SeparatorMenu),
new MenuItem(Resources.ExitMenu, ExitMenuClick),
new MenuItem(Resources.WindowsMenu) { DefaultItem = true, BarBreak = true },
new MenuItem(Resources.SeparatorMenu)
});
}

#endregion Private method for context menu creation

Expand Down Expand Up @@ -141,6 +162,32 @@ private static void EnableMenuItem(MenuItem mi)

#endregion Private helper methods for menu items

private void ShowWindowList()
{
ContextMenu.Show(_invisibleWindow, new System.Drawing.Point());
}

#region Private helper window for context menu

private class InvisibleWindowForContextMenu : Form
{
public InvisibleWindowForContextMenu(ContextMenu contextMenu)
{
StartPosition = FormStartPosition.Manual;
Width = 0;
Height = 0;
Top = 9000;
Left = 9000;
Visible = true;
ControlBox = false;
ShowIcon = false;
ShowInTaskbar = false;
ContextMenu = contextMenu;
}
}

#endregion Private helper window for context menu

#region IDisposable methods

/// <summary>
Expand Down
1 change: 1 addition & 0 deletions Properties/App.config
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
</startup>
<appSettings>
<add key="GlobalHotkey" value="Ctrl + Alt + Z"/>
</appSettings>
</configuration>
4 changes: 2 additions & 2 deletions Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("43A57E91-1D03-448D-857C-75B3A81F69A7")]
[assembly: AssemblyVersion("0.1.4.*")]
[assembly: AssemblyFileVersion("0.1.4")]
[assembly: AssemblyVersion("0.1.5.*")]
[assembly: AssemblyFileVersion("0.1.5")]
[assembly: NeutralResourcesLanguage("en-US")]

#endregion Assembly information
18 changes: 18 additions & 0 deletions Resources/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Resources/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ Free for all.</value>
<data name="AboutMenu" xml:space="preserve">
<value>About HardTop</value>
</data>
<data name="CloseMenuMenu" xml:space="preserve">
<value>Close menu</value>
</data>
<data name="DonationMenu" xml:space="preserve">
<value>Web site / Donate</value>
</data>
Expand Down Expand Up @@ -155,6 +158,9 @@ Please report to feedback@voltura.se!</value>
<data name="SettingsMenu" xml:space="preserve">
<value>Settings</value>
</data>
<data name="ShowMenuMenu" xml:space="preserve">
<value>Show menu</value>
</data>
<data name="StartWithWindows" xml:space="preserve">
<value>HardTop set to start with Windows</value>
</data>
Expand Down
2 changes: 2 additions & 0 deletions Settings.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#region Using statements

using Microsoft.Win32;
using System;
using System.Configuration;
using System.IO;
using System.Windows.Forms;
Expand Down Expand Up @@ -72,6 +73,7 @@ private static void CreateSettings()
string xml = $@"<?xml version=""1.0"" encoding=""utf-8"" ?>
<configuration>
<appSettings>
<add key=""GlobalHotkey"" value=""Ctrl + Alt + Z""/>
</appSettings>
</configuration>";
File.WriteAllText(settingsFile, xml, System.Text.Encoding.UTF8);
Expand Down

0 comments on commit eeacc44

Please sign in to comment.