Hook KeyboardEvent when command is running

Hi, I have a plugin command that consists in a modeless form. It is a radial menu developed in c#.
I want the user be able to activate some menu command with keyboard.

I tried to use KeyboardEvent of the RhinoApp object but I’ve two concerns

  1. The key code I get in the event handler doesn’t correspond to the documentation at https://developer.rhino3d.com/api/rhinocommon/rhino.ui.keyboardkey
  • For exemple, when I press “Q”, the key code in the event handler is “0” (zéro)
  • What are the key codes on MacOS platform ?
  1. I want when my plugin command is running (i.e. When the menu is opened) to hook Rhino keyboard events, and prevent pressed key to be added to Rhino command line. Is there a way to achieve this ?
  • I can “hook” key press event with KeyboardEvent, BUT each char appears in the Rhino command Textbox
  • I want to disable this behavior while my menu is showing, but I just can’t find a solution. Below what I tried, but without success
    • “RhinoApp.CommandWindowCaptureEnabled” with no success, it doesn’t disable capture
    • Flush or write empty string with RhinoApp.CommandLineOut.Flush or RhinoApp.CommandLineOut.Write but it does nothing (I think because the pressed key is sent to the command line after the KeyboardEvent handler)

Thank for your help :slight_smile:

@CallumSykes can you advise?

Hey @Tigrou,

Love the project! Very excited to see the progress, and very cool it’s open source.
I tried to build your project, but couldn’t seem to. Some instructions for how to do that would be very helpful :slight_smile: I think I might be missing some required AppKit dependencies?

Since you have a UI based on an Eto.Forms.Form I’d recommend overriding OnKeyDown for your form and then you can capture the key down and prevent it ending up in the Command Line.

Something like the below

protected override void OnKeyDown(KeyEventArgs e)
{
    e.Handled = true;
    base.OnKeyDown(e);
    // ... Your Code Here
}

Hi @CallumSykes ,
thanks for your quick reply, but I think I wasn’t clear.
I have no problem to capture key press when the Eto.Form has focus. BUT I also want to capture key events when the form has no focus (I can with Rhino KeyboardEvent) but in this case, each keyboard key press is also inputed in the Rhino command line → I don’t want letters to be input in the Rhino command line if my menu is showing

  • this is to give user hability to trigger plugin menu items with keyboard, something like instant alias in rhino 9 WP :slight_smile:

For your problem for compiling the code, you can log an issue on the GitHub repo :sunglasses:
The only project dependency is NLog (can be found in Nuget).

1 Like

Ah I understand now.

I do see this too, I don’t believe these integers correspond to this enum (at least on mac), as you have found.

As I understand it, there is no public way in the C# SDK to prevent keys going to the command line without a UI that has focus (as I showed above).

This API toggle enables the developer to capture text input, but does not affect the functionality of the Command Line. The other items your mentioned work in a similar space.

As for the issue, logged here.

Damn :frowning: !
I’ll look into another way to give focus back to the menu, maybe something like press “command” or “control”.
But weird, when I press command, I see that Rhino send the event twice in event handler KeyboardEvent, this does not happen with “normal” keys like A,B etc… Do you know why ?

For the issue on GitHub, I answer. Hope it will help you to compile. So I ask you to give me some dotnet command output to be sure of my answer :slight_smile:

1 Like

I’m not sure why this is sorry.

No problem, I found a workaround in keeping last “special” key event and check if the event handler receive the same :slight_smile:

1 Like

Having a hard time repeating an example of this but I’ve also found on Windows that Ctrl key in keyboard events can cause duplicate/twice logic sometimes.

Still trying to figure out why it’s happening and will report back if I do find that.

Hi,

finally found a hack to capture keyboard event before Rhino and avoid Rhino to add keyboard chars into its command line.

Exemple below in an Eto.Form constructor named “SectorRadialMenuForm”.
The hack consists in changing first and next responder of main Rhino Window to the Eto.Form window.

I know it breaks Rhino behavior, but it works.

Next step is to be able to “relay” the keyboard event to Rhino if the Eto.Form (or plugin) doesn’t want to handle the event.

public SectorRadialMenuForm(PlugIn plugin) : base(plugin)
        {
            var mainWin = RhinoEtoApp.MainWindow; // Main rhino window
            var nswin = (NSWindow)mainWin.ControlObject; // Get native MacoOS Window object
            if (nswin != null)
            {
                var res = nswin.ResignFirstResponder(); // Ask main rhino win to resign his first responder
                var formNswin = (NSWindow)this.ControlObject; // Eto form MacOS native NSWindow object
                nswin.MakeFirstResponder(formNswin); // Make current « SectorRadialMenuForm » the first responder
                nswin.NextResponder = formNswin; // IMPORTANT : Set current etc form window the next responder (without it, it just don’t work)
            }
            this.KeyDown += (s, e) =>
            {
                var a = 0; // Here we can respond to keyboard event before Rhino main window. No keyboard char is added to Rhino command
            };
            this.KeyUp += (s, e) =>
            {
                var a = 0;// Same as « KeyDown » event but when key is released
            };
	}

Quick update:

to send the keyboard event to Rhino, it is very simple

  • Keep a ref to previous “nextResponder”
  • Set the Eto.Form “nextResponder” to the original Rhino window “nextResponder”
  • in key down/up event handler in Eto.Form, just update the Handled property of the event. If “true”, the event will NOT be “transferred” to rhino, if “false” the event will be “transferred” to rhino

/!\ I know this is a hack and we should not do this. But there is no rhino API

Exemple code of my previous post

public SectorRadialMenuForm(PlugIn plugin) : base(plugin)
        {
            var mainWin = RhinoEtoApp.MainWindow;
            var nswin = (NSWindow)mainWin.ControlObject;
            if (nswin != null)
            {
                var res = nswin.ResignFirstResponder();
                var formNswin = (NSWindow)this.ControlObject;
                nswin.MakeFirstResponder(formNswin);
                mainRhinoNextResponder = nswin.NextResponder; // Keep ref to rhino main windows next responder
                nswin.NextResponder = formNswin;
                formNswin.NextResponder = mainRhinoNextResponder; // Set Eto.Form next responder to Rhino window NextResponder so the event can be "transferred" to rhino
            }
            this.KeyDown += (s, e) =>
            {
                e.Handled = true; // Rhino will not receive the event
                e.Handled = false; // Rhino will receive the event
            };
            this.KeyUp += (s, e) =>
            {
                e.Handled = true;
            };
      }