Open retailcoder opened 8 years ago
I have implemented a hotkey in past, not on low level though. Basically it was like a Collection of keys and then removing items from collection when it matches the current key press. As soon as the collection becomes empty I would raise hotkey press event. When its not empty after the number of keys expected to be pressed together consecutively I would repopulate the collection.
Just an idea and I would be happy to merge whatever the way you would like to implement it. For long I wanted to add some comments and cleanup the code, never happened.
Okay... this is harder than I expected :smiley:
@justcoding121 so I implemented what should be a HotkeyHook
, but I'm getting lost into implementation details and levels of indirection - feel free to take a look here...
In Rubberduck I'm hooking the hotkeys using the VBE MainWindow's hWnd
- I'm not sure how to fit that into your API, so for now I'm having that IntPtr hWnd
passed as a constructor parameter to the HotkeyHook
.
The API I'm shooting for is made obvious with the HotkeyHook.Register(Keys, ICommand)
method, but I'm not sure it's at the right level of abstraction - still, the idea is that the client code would call Register
and supply the key combination along with an ICommand
implementation (that's the standard System.Windows.Input.ICommand
interface), and when Start
is called, the registered combos get actually registered as hotkeys, and when the WM_HOTKEY
message is picked up, it runs the command if the command's CanExecute
method returns true
.
Or perhaps it could be made more generic, and the hotkey could be registered with any Action
delegate to be invoked... I've been using an ICommand
in my project, so it fits my purposes, but this is a library, and thinking again asking the client code to have an ICommand
to run might not be ideal. ... actually perhaps it's best to just raise an event saying "hotkey Ctrl+R was pressed" and let the client code deal with the rest - it's just that having the ICommand
there, avoids having to figure out the Keys
value to pass back to the client in the HotkeyEventArgs
.
Anyway it's a "first pass", and a lot of it is a lousy copy/paste job moving code from Rubberduck and then tweaking it... let me know what you think (even if that's "ew thanks but no thanks"!)
Forgot to mention - I'd like the thing to support "chords" / 2-step hotkeys at one point, e.g. Ctrl+R,R
for Refactor/Rename. The watcher should then fire up an event for the client to report something like "Ctrl+R was pressed. Awaiting second key..." - and then run the command (/raise that event) when the 2nd key combo is pressed. I just haven't figured out a good way to register and handle those yet.
I agree that we should use an action delegate, but if you can isolate the changes for hotkey and PR to a feature branch I can work on integrating the shared hwnd, asynchronous message handling & delegates.
Since we don't really know if this library will be used in a console app or an app with a message loop I use the helper class to lazily create a hWnd and a message loop. Its been reused for KeyboardHook, MouseHook, ClipboardHook & ApplicationWatcher since all of them needs to be run in a thread with a message loop.
Beautiful work! I think this library would be more complete if it also handled hotkeys; the API could intake a
Keys
flag enum value for the key combination, and internally work out the applicable+^%
modifier string... or perhaps intake the hotkey string as an overload.It also needs a way to identify hotkeys, without forcing a type on the client code -
object
would work, but a generic type argument would be even better. Something like this:When a hotkey message is captured, an event is raised and the corresponding "key" that the hotkey was registered with is sent back to the client code, who can then decide what to do.
Note: I'll submit a PR soon-ish :-)