picoe / Eto

Cross platform GUI framework for desktop and mobile applications in .NET
Other
3.57k stars 325 forks source link

Inconsistent behavior for KeyDown event handler and KeyEventArgs across platforms and calls #2467

Open somelinguist opened 1 year ago

somelinguist commented 1 year ago

Hi, I've come across some unexpected behavior across platforms when key events.

Expected Behavior

  1. A handler for the KeyDown event should be called the same number of times in the same situations for all platforms.

  2. The value of KeyEventArgs properties should be compatible across platforms.

For example, the value of keyEvent.IsChar when keyEvent.Key is Keys.Right or Keys.G should be the same on Windows and Mac.

  1. The value of KeyEventArgs properties should be consistent across calls when the same key is pressed.

For example, the value of keyEvent.IsChar when keyEvent.Key is Keys.Right or Keys.G should be the same across all calls of the registered handler.

Actual Behavior

  1. On WPF or WinForms, a handler for KeyDown for a TextArea is called twice when text is input into the TextArea as a result of the event, but only once when no text is input. On Mac, however, the same handler is called only once in both situations.

  2. On Mac, KeyEventArgs.IsChar is always true, even when KeyEventArgs.Key is something like Keys.Right, and thus KeyEventArgs.KeyChar always has a value (at least in my testing...several other unexpected keys too).

On Mac, this is due to this line:

https://github.com/picoe/Eto/blob/ea1d236d4883d0d56daa480ebb9a979090b25321/src/Eto.Mac/MacConversions.cs#L359

This is because theEvent.Characters usually/always has a value. See https://developer.apple.com/documentation/appkit/nsevent/1533943-keyevent and https://developer.apple.com/documentation/appkit/nsevent/1535851-function-key_unicode_values .

On WPF and WinForms, however, for the first call of the KeyDown handler KeyEventArgs.IsChar is always false, even when KeyEventArgs.Key is something like Keys.G.

This is because in the code that converts the native event (WPF) args to Eto KeyEventArgs, nothing is passed to the keyChar parameter:

https://github.com/picoe/Eto/blob/ea1d236d4883d0d56daa480ebb9a979090b25321/src/Eto.Wpf/WpfConversions.cs#L138-L145

  1. On WPF and WinForms, the two calls made to a TextArea's KeyDown handler have different values for KeyEventArgs.IsChar and KeyEventArgs.KeyChar.

For the first call, as in (2) above, KeyEventArgs.IsChar is always false and KeyEventArgs.KeyChar is null. Also, KeyEventArgs.Key has a relevant value.

If there is a second call, however, KeyEventArgs.IsChar is true because text is inserted, and KeyEventArgs.KeyChar has an appropriate value (at least for the system). In this call, KeyEventArgs.Key is set to Keys.None.

For the second (and subsequent?) call, this is so because the code for HandleTextInput (WPF) creates aKeyEventArgs for each character input, with the value of each character passed to the keyChar parameter.

https://github.com/picoe/Eto/blob/ea1d236d4883d0d56daa480ebb9a979090b25321/src/Eto.Wpf/Forms/WpfFrameworkElement.cs#L766-L783

Steps to Reproduce the Problem

  1. Register a KeyDown handler for a TextArea
  2. Create targets for WPF, WinForms, and MacOS

Code that Demonstrates the Problem

Here's a simple handler (in F#) that logs the values of KeyEventArgs.Key, KeyEventArgs.IsChar, and KeyEventArgs.KeyChar.

        let textArea = new TextArea()

        textArea.KeyDown.Add (fun k ->
            printfn $"{k.Key}"
            printfn $"{k.IsChar}"
            printfn $"{k.KeyChar}"
        )

The combination of these differences between platforms is making handling KeyEvents difficult. :) I'm not sure what to do about it. I haven't gotten as far as testing GTK, but I'm hoping the app I'm working on will eventually target it as well.

Specifications