-
Notifications
You must be signed in to change notification settings - Fork 6k
[macOS, Keyboard] Derive keyboard layout using printable information from system #32152
Conversation
|
This pull request is not mergeable in its current state, likely because of a merge conflict. Pre-submit CI jobs were not triggered. Pushing a new commit to this branch that resolves the issue will result in pre-submit jobs being scheduled. |
|
From PR review triage: Ping @gspencergoog |
|
The lint failures in presubmit look related to this PR: |
gspencergoog
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
|
||
| // Someohow this pointer type must be defined as a single type for the compiler | ||
| // to compile the function pointer type (due to _Nullable). | ||
| typedef NSResponder* _NSResponderPtr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This appears to be duplicated from above, and the _NSResponderPtr too.
| #endif | ||
| // The logical key should be the first available clue from below: | ||
| // | ||
| // - Mandatory goal, if matches any clue. This ensures that all alnum keys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // - Mandatory goal, if matches any clue. This ensures that all alnum keys | |
| // - Mandatory goal, if it matches any clue. This ensures that all alnum keys |
| // Record embedder calls to the given storage. | ||
| // | ||
| // They are not responded to until the stored callbacks are manually called. | ||
| // They are not to responded until the stored callbacks are manually called. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, "responded to" was correct.
| namespace flutter::testing { | ||
|
|
||
| namespace { | ||
| using namespace flutter::testing::keycodes; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't use using namespace, it's against the style guide:
It's okay to use using for a particular symbol or symbols.
| }; | ||
|
|
||
| /** | ||
| * Returns the current unicode layout data (kTISPropertyUnicodeKeyLayoutData). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| * Returns the current unicode layout data (kTISPropertyUnicodeKeyLayoutData). | |
| * Returns the current Unicode layout data (kTISPropertyUnicodeKeyLayoutData). |
…rmation from system (flutter/engine#32152)
…rmation from system (flutter/engine#32152)

This PR changes how Flutter derives logical keys on macOS. By gathering the keyboard layout information and analyzing them as a whole, Flutter can build the key map and fix problems that can not be overcome by calculating on a per-key basis.
This addresses the first phase of flutter/flutter#100456 on macOS, but requires flutter/flutter#100803 to apply to RawKeyboard and Shortcut.
Effect
With this PR, it's guaranteed that there are always keys that are mapped to each of the 10 digit keys and 26 alphabet keys. Additionally, Shift-Digit keys now produces digits just like when they're unmodified, instead of symbols. Examples:
digit1digit1(unchanged)shiftLeft + exclamationshiftLeft + digit1ampersanddigit1shiftLeft + digit1shiftLeft + digit1(unchanged)фkeyAъbracketRightOther keys that are not related to alnums are not changed.
Resulting French layout:

Resulting Russian layout:

There is a duplicate key, which we should resolve in the future.
How?
Mac actually has a function that tells you "if you press this key with this modifier state and dead key state right now, you will get this character". By traversing through this function on all key codes, Flutter can get a full picture without key events, which is important because Flutter need to know "are there any keys that produces character 'a'?" before deciding which key to map to KeyA.
The tricky part is that this requires functions only available in Carbon, which Apple has been announced as deprecated, and even removed from documentation since Catalina. However, Chromium and Electron still uses it (here), as well as all code examples (as far as I can see) regarding this feature. I've discussed with @chinmaygarde, who approved it since it's not a private function.
Detail logic
For each key code, derives its "layout clue", which is the printable character when it's pressed, and when it's pressed with Shift.
[and]toхandъrespectively. To avoid returning any non-latin keys (because no one will use them), we map them back to US layout.Beautiful?
The way to pass data around is kind of ugly. This PR adds a new property to
PrimaryKeyResponderto notify responders of the map, and also a new fieldspecifiedLogicalKeyin RawKeyEventMacos's message, making it less "raw". Partly it's because we have too little time to migrate unit tests for a "beautiful" design, but also I can't think of other ways for thespecifiedLogicalKeyfield except for a different name.Breaking?
This change can be considered breaking, because it changes what keys are dispatched under certain conditions. But people have been complaining about unintuitive logical key behavior and broken shortcuts. Additionally, this PR aligns the behavior of macOS with that on Windows. So, we can also consider this a fix.
Future work
This changes have not changed the mapping strategy for symbol keys. For example,
=is mapped toequal, butShift + =is mapped toplus, which can be confusing. Also, some keyboards have dead keys for both clues.Finding a strategy for these keys is harder and might be more controversial. We'll leave it for the future.
Pre-launch Checklist
writing and running engine tests.
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.