Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework keyboard event / keyboard shortcut #338

Merged
merged 14 commits into from May 25, 2020
Merged

Conversation

pchampio
Copy link
Member

@pchampio pchampio commented Jan 12, 2020

fixes: #314
fixes: #332
fixes: #358

Uses UTF-16 for textencoding: ref

@pchampio pchampio changed the title don't send key event when the textinput has client [WIP] don't send key event when the textinput has client Jan 14, 2020
@pchampio pchampio changed the title [WIP] don't send key event when the textinput has client [WIP] Rework keyboard event / keyboard shortcut Jan 15, 2020
key-codepoint.cpp Outdated Show resolved Hide resolved
@pchampio pchampio mentioned this pull request Apr 4, 2020
@james-lawrence
Copy link
Contributor

james-lawrence commented Apr 5, 2020

  • shift + arrows to select text.
  • delete works
  • shift return
  • return
  • ctrl + delete - removed 1 character.
  • copy/paste

keyboard_events demo didn't work at all:

An Observatory debugger and profiler on Flutter test device is available at: http://127.0.0.1:50300/
For a more detailed help message, press "h". To detach, press "d"; to quit, press "q".
flutter: ══╡ EXCEPTION CAUGHT BY SERVICES LIBRARY ╞══════════════════════════════════════════════════════════
flutter: The following _Exception was thrown during a platform message callback:
flutter: Exception: Unsupported platform RawKeyEventDataMacOs
flutter: 
flutter: When the exception was thrown, this was the stack:
flutter: #0      _KeyboardTestPageState.onKeyEvent (package:keyboard_event/app.dart:83:9)
flutter: #1      _RawKeyboardListenerState._handleRawKeyEvent (package:flutter/src/widgets/raw_keyboard_listener.dart:121:14)
flutter: #2      RawKeyboard._handleKeyEvent (package:flutter/src/services/raw_keyboard.dart:528:17)
flutter: #3      BasicMessageChannel.setMessageHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:74:49)
flutter: #4      _DefaultBinaryMessenger.handlePlatformMessage (package:flutter/src/services/binding.dart:200:33)
flutter: #5      _invoke3.<anonymous closure> (dart:ui/hooks.dart:303:15)
flutter: #9      _invoke3 (dart:ui/hooks.dart:302:10)
flutter: #10     _dispatchPlatformMessage (dart:ui/hooks.dart:162:5)
flutter: (elided 3 frames from package dart:async)
flutter: ════════════════════════════════════════════════════════════════════════════════════════════════════
flutter: Another exception was thrown: Exception: Unsupported platform RawKeyEventDataMacOs

@pchampio
Copy link
Member Author

pchampio commented Apr 6, 2020

I've update the keyboard_events accordingly. The issue was in the example.
ctrl + delete is handled by the flutter framework, I don't want to support Ctrl+delete.

The 2 other points needs to be address before merging this PR.

@pchampio
Copy link
Member Author

pchampio commented Apr 6, 2020

I'm still a bit surprised by the fact that shift+arrow doesn't select text. :/
The text navigation, copy/paste combinations that I specified in the readme are for linux/windows.

Are the MacOS one working https://i.imgur.com/YPpx9Px.png working?
When I simulate an MacOS environment, everything seems to work 😞

https://transfer.sh/dynwB/dell_xps_pchampion.mp4

@james-lawrence
Copy link
Contributor

shift, control, and command results in the following:

flutter: ══╡ EXCEPTION CAUGHT BY SERVICES LIBRARY ╞══════════════════════════════════════════════════════════
flutter: The following assertion was thrown during a platform message callback:
flutter: Platform key support for macos is producing unsupported modifier combinations.
flutter: 'package:flutter/src/services/raw_keyboard.dart':
flutter: Failed assertion: line 580 pos 14: 'et<LogicalKeyboard'
flutter: 
flutter: Either the assertion indicates an error in the framework itself, or we should provide substantially
flutter: more information in this error message to help you determine and fix the underlying cause.
flutter: In either case, please report this assertion by filing a bug on GitHub:
flutter:   https://github.com/flutter/flutter/issues/new?template=BUG.md
flutter: 
flutter: When the exception was thrown, this was the stack:
flutter: #2      RawKeyboard._synchronizeModifiers (package:flutter/src/services/raw_keyboard.dart:580:14)
flutter: #3      RawKeyboard._handleKeyEvent (package:flutter/src/services/raw_keyboard.dart:522:5)
flutter: #4      BasicMessageChannel.setMessageHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:74:49)
flutter: #5      _DefaultBinaryMessenger.handlePlatformMessage (package:flutter/src/services/binding.dart:200:33)
flutter: #6      _invoke3.<anonymous closure> (dart:ui/hooks.dart:303:15)
flutter: #10     _invoke3 (dart:ui/hooks.dart:302:10)
flutter: #11     _dispatchPlatformMessage (dart:ui/hooks.dart:162:5)
flutter: (elided 5 frames from class _AssertionError and package dart:async)
flutter: ════════════════════════════════════════════════════════════════════════════════════════════════════

Copy link
Contributor

@james-lawrence james-lawrence left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overall LGTM, not sure how I feel about runtime.GOOS == "darwin" vs using build tags and methods.

will need the change I opened a PR for to work properly on macs though.

@@ -0,0 +1,559 @@
package glfwkeyconversion

import "github.com/go-gl/glfw/v3.3/glfw"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we may be better served by making this platform specific.

internal/keyboard/keyboard_darwin.go // has all the mappings defined in these two files.
internal/keyboard/keyboard_unix.go // with a +build unix !darwin
internal/keyboard/keyboard_windows.go

each file with a single method:

func Normalize(keycode glfw.Key, scancode int) Event { /* ... */ }

// internal/keyboard/keyboard.go
// replaces keyEventMessage
type Event struct {
  // ...
}

key-events.go Outdated
utf8 := glfw.GetKeyName(key, scancode)
var event keyEventMessage

if runtime.GOOS == "darwin" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we do the build tags this code would collapse to:

event := keyboard.Normalize(key, scancode)
if err := p.keyEventChannel.Send(event); err != nil { /* .... */ }

key-events.go Outdated
// code point will be used. There is unlikely to be more than one, but there
// is no guarantee that it won't happen.
func CodepointFromGLFWKey(utf8 []rune) uint32 {
return uint32(utf8[0])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets handle the zero length here and return 0.

textinput.go Outdated
return
}
// Word Backspace
if (runtime.GOOS == "darwin" && mods == glfw.ModAlt) || (runtime.GOOS != "darwin" && mods == glfw.ModControl) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if keyboard.DetectDeleteWord(key, mods) {
  /* ... */
}


// AsMacOSModifiers translate the keycode to the ModifierKey
func AsMacOSModifiers(keycode glfw.Key) (int, bool) {
val, ok := modifierKeytoMods[keycode]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a simple switch statement is likely more performant. could be wrong here =)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an interesting topic. According to this blogpost the map is actually faster, but not much difference in absolute times. https://hashrocket.com/blog/posts/switch-vs-map-which-is-the-better-way-to-branch-in-go

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I ran across that which is why I reverted my thinking.

its actually an interesting optimization that could be done to the switch statements at the compiler level.

I suspect its because maps are hashing the key which is a fixed cost based on the key length. then doing a lookup. whereas a switch statement is doing multiple comparisons between the key and the case values.


// ToMacOSModifiers takes a glfw ModifierKey and return his MacOS equivalent
// as defined in https://github.com/flutter/flutter/blob/3e63411256cc88afc48044aa5ea06c5c9c6a6846/packages/flutter/lib/src/services/raw_keyboard_macos.dart#L241
func ToMacOSModifiers(mods glfw.ModifierKey) int {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removes the need to initialize it below.

Suggested change
func ToMacOSModifiers(mods glfw.ModifierKey) int {
func ToMacOSModifiers(mods glfw.ModifierKey) (macOSmods int) {

The flutter framework sends utf16 vectors
textinput.go Outdated
@@ -104,7 +105,7 @@ func (p *textinputPlugin) handleSetEditingState(arguments interface{}) (reply in
return nil, errors.Wrap(err, "failed to decode json arguments for handleSetEditingState")
}

p.word = []rune(editingState.Text)
p.word = utf16.Encode([]rune(editingState.Text))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utf16? Is that what Flutter uses?
I'd think utf8 would be the default for modern projects.... Especially with the "utf-8 everywhere" movement that's going on.
http://utf8everywhere.org/

Copy link
Member Author

@pchampio pchampio Apr 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utf16? Is that what Flutter uses?

Yes, that what uses Dart to be more specific!

Here is some readings:

A bug we encounter: #314 due to a miss match between utf8 and utf16 indexes.

I'd think utf8 would be the default for modern projects.... Especially with the "utf-8 everywhere" movement that's going on.

Well, the glfw callback returns a utf8 code point, we then convert it to utf16 at the embedder level and then send it to flutter.
On the outside, we support as input utf8-based code point 😄... but on the inside.. it'sa Roller coaster casting character 🪂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In for the ride, choo choo! 🎢

@pchampio pchampio requested a review from GeertJohan May 22, 2020 20:35
@pchampio pchampio changed the title [WIP] Rework keyboard event / keyboard shortcut Rework keyboard event / keyboard shortcut May 22, 2020
@pchampio
Copy link
Member Author

Ready for another review/fix cycle!

return nil, errors.Wrap(err, "failed to decode json arguments for handleSetClient")
}

err = json.Unmarshal(args[0], &p.clientID)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Length of 'args' is never checked, will cause panic if the message format ever changes.

key-events.go Outdated
}
err := p.keyEventChannel.Send(event)
if err != nil {
if err := p.channel.Send(event); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to never inline the assignment to err unless it's a function that only fetches some information or performs a check. In this case, the call to Send is actually important for the function a a whole, not just for this error check, so it feels off to inline it. But that could just be personal preference.

}
// Opinionated: If a flutter dev uses TextCapitalization.characters
// in a TextField, that means he wants only to receive
// uppercase characters.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. In some cases on Android it may be possible to switch back from the uppercased keyboard to the lowercased one... Although indeed that may not be the intention of the developer. We'll have to see how this lands with go-flutter users. For now I think this is the best approach yes.

Copy link
Member

@GeertJohan GeertJohan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a missing check on length of a slice.

Otherwise LGTM!

@pchampio pchampio requested a review from GeertJohan May 25, 2020 11:31
@pchampio
Copy link
Member Author

Ready for another review/fix cycle!

@pchampio pchampio merged commit 51326a1 into master May 25, 2020
@pchampio pchampio deleted the fix_keyboard_shortcut branch May 25, 2020 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
3 participants