ruffle
ruffle copied to clipboard
web: Implement basic IME
This patch implements IME preediting and committing on web. It does not implement moving the cursor and proper positioning yet.
- Progresses https://github.com/ruffle-rs/ruffle/issues/1778
This isn't working on Android when I try to type in an EditText with the virtual keyboard.
A virtual keyboard like the one on Android isComposing.
To explain the current logic, which I guess may need comments.
- If you type a single character using the virtual keyboard, that character fires a keydown and then keyup event into the focused EditText.
- If you backspace or delete a single character using the virtual keyboard, that backspace/delete fires a keydown and then keyup event into the focused EditText.
- If you paste a string using the virtual keyboard, each character in that string in sequence fires a keydown and then keyup event into the focused EditText.
I'm guessing the IME logic can be used to support this more properly, but landing this PR without IME support would regress the virtual keyboard.
Maybe we can do exactly this but also keydown and keyup the event.data character(s) on compositionend.
@danielhjacobs can you check if the current code works properly? It does for me
Tried with GBoard and it worked perfectly. With Samsung Keyboard it's unfortunately a different story, see recording.
https://github.com/user-attachments/assets/d3b00611-e2df-4131-8584-3868ea23ead2
It seems that this keyboard uses IME for inputting all text. Without implementing IME on web we cannot have both IME preview and IME input working :/ This PR breaks IME preview, but fixes IME input.
Okay, as IME on web is a mess, I've decided to implement basic IME mechanics (including preediting) on web.
@danielhjacobs @n0samu You can test it out here: https://kjarosh.github.io/ruffle/pr19896/
As stated on Discord, but putting here for future people, this is now working with Samsung Keyboard.
@jmousy Could you check if it works properly? It's available at https://kjarosh.github.io/ruffle/pr19896/
This is a summary of the chat on Discord. Tested across OS and keyboard software on the same text and flash file.
- '*' is cursor
- 'Default' means using a regular hardware keyboard.
- This is the result when you type "가나다" (rkskek, ㄱ + ㅏ + ㄴ + ㅏ + ㄷ + ㅏ).
- Tested with the following flash files: 흥해라편의점.zip
- Windows: Windows 10 24H2 & Google Chrome 135
- macOS: macOS 15.3.2 & Google Chrome 135
- iOS: iPhone 13 Pro iOS 18.4 & Safari
- Android: Samsung Galaxy Z Fold 6 & Android 14 & One UI 6.1.1 & Samsung Internet
- Linux: Ubuntu 24.04.2 LTS & Firefox latest (with VMware virtual machine)
| OS | Keyboard Type | Result | Other Issue |
|---|---|---|---|
| iOS | Default | ㄱㅏㄴㅏㄷㅏ* | |
| iOS | GBoard | The text you entered appears in the following order and then disappears: ㄱ-가-간-{empty}-낟-{empty} | |
| Android | Samsung Keyboard | 가나다* | |
| Android | GBoard | 가나다* | If you try to type "123가나다", it will be entered as "123ㄱㅏ나다". |
| Windows | Default | 가나다* | If you click on the screen after entering text "가나다", an extra "다" is entered: "가나다다" |
| macOS | Default | ㄱ가나다*ㅏㄷㅏㄴㅏ | The cursor will be positioned after "다" not the end. Also, if you type only "ㄱ", it will output "ㄱㄱ". |
| Linux | Default | 가나다* |
Below is the output from the link below, as requested by Daniel Jacobs on Discord: https://codepen.io/danieljacobs/pen/ByaMLpL
Input text: '가나다' (rkskek)
iOS 18.4 (default keyboard)
keydown - key: ㄱ, data: N/A, inputType: N/A, isComposing: N/A
keypress - key: ㄱ, data: N/A, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: ㄱ, inputType: insertText, isComposing: N/A
input - key: N/A, data: ㄱ, inputType: insertText, isComposing: N/A
keyup - key: ㄱ, data: N/A, inputType: N/A, isComposing: N/A
keydown - key: ㅏ, data: N/A, inputType: N/A, isComposing: N/A
keypress - key: ㅏ, data: N/A, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: ㅏ, inputType: insertText, isComposing: N/A
input - key: N/A, data: ㅏ, inputType: insertText, isComposing: N/A
keyup - key: ㅏ, data: N/A, inputType: N/A, isComposing: N/A
keydown - key: ㄴ, data: N/A, inputType: N/A, isComposing: N/A
keypress - key: ㄴ, data: N/A, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: ㄴ, inputType: insertText, isComposing: N/A
input - key: N/A, data: ㄴ, inputType: insertText, isComposing: N/A
keyup - key: ㄴ, data: N/A, inputType: N/A, isComposing: N/A
keydown - key: ㅏ, data: N/A, inputType: N/A, isComposing: N/A
keypress - key: ㅏ, data: N/A, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: ㅏ, inputType: insertText, isComposing: N/A
input - key: N/A, data: ㅏ, inputType: insertText, isComposing: N/A
keyup - key: ㅏ, data: N/A, inputType: N/A, isComposing: N/A
keydown - key: ㄷ, data: N/A, inputType: N/A, isComposing: N/A
keypress - key: ㄷ, data: N/A, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: ㄷ, inputType: insertText, isComposing: N/A
input - key: N/A, data: ㄷ, inputType: insertText, isComposing: N/A
keyup - key: ㄷ, data: N/A, inputType: N/A, isComposing: N/A
keydown - key: ㅏ, data: N/A, inputType: N/A, isComposing: N/A
keypress - key: ㅏ, data: N/A, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: ㅏ, inputType: insertText, isComposing: N/A
input - key: N/A, data: ㅏ, inputType: insertText, isComposing: N/A
keyup - key: ㅏ, data: N/A, inputType: N/A, isComposing: N/A
Android 14 (samsung keyboard)
keydown - key: Unidentified, data: N/A, inputType: N/A, isComposing: N/A
compositionstart - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
compositionupdate - key: N/A, data: ㄱ, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: ㄱ, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: ㄱ, inputType: insertCompositionText, isComposing: true
keyup - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
keydown - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 가, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
keyup - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
keydown - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 간, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 간, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 간, inputType: insertCompositionText, isComposing: true
keyup - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
keydown - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 가, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
compositionend - key: N/A, data: 가, inputType: N/A, isComposing: N/A
keyup - key: Unidentified, data: N/A, inputType: N/A, isComposing: N/A
keydown - key: Unidentified, data: N/A, inputType: N/A, isComposing: N/A
compositionstart - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
compositionupdate - key: N/A, data: 나, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 나, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 나, inputType: insertCompositionText, isComposing: true
keyup - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
keydown - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 낟, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 낟, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 낟, inputType: insertCompositionText, isComposing: true
keyup - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
keydown - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 나, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 나, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 나, inputType: insertCompositionText, isComposing: true
compositionend - key: N/A, data: 나, inputType: N/A, isComposing: N/A
keyup - key: Unidentified, data: N/A, inputType: N/A, isComposing: N/A
keydown - key: Unidentified, data: N/A, inputType: N/A, isComposing: N/A
compositionstart - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
compositionupdate - key: N/A, data: 다, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 다, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 다, inputType: insertCompositionText, isComposing: true
keyup - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
compositionend - key: N/A, data: 다, inputType: N/A, isComposing: N/A
Android (GBoard)
keydown - key: Unidentified, data: N/A, inputType: N/A, isComposing: N/A
compositionstart - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
compositionupdate - key: N/A, data: ㄱ, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: ㄱ, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: ㄱ, inputType: insertCompositionText, isComposing: true
keyup - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
keydown - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 가, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
keyup - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
keydown - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 간, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 간, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 간, inputType: insertCompositionText, isComposing: true
keyup - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
keydown - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 가나, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 가나, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 가나, inputType: insertCompositionText, isComposing: true
keyup - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
keydown - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 가낟, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 가낟, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 가낟, inputType: insertCompositionText, isComposing: true
keyup - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
keydown - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 가나다, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 가나다, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 가나다, inputType: insertCompositionText, isComposing: true
keyup - key: Unidentified, data: N/A, inputType: N/A, isComposing: true
compositionend - key: N/A, data: 가나다, inputType: N/A, isComposing: N/A
Windows 11
keydown - key: Process, data: N/A, inputType: N/A, isComposing: N/A
compositionstart - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
compositionupdate - key: N/A, data: ㄱ, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: ㄱ, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: ㄱ, inputType: insertCompositionText, isComposing: true
keyup - key: Process, data: N/A, inputType: N/A, isComposing: true
keyup - key: r, data: N/A, inputType: N/A, isComposing: true
keydown - key: Process, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 가, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
keyup - key: Process, data: N/A, inputType: N/A, isComposing: true
keyup - key: k, data: N/A, inputType: N/A, isComposing: true
keydown - key: Process, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 간, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 간, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 간, inputType: insertCompositionText, isComposing: true
keyup - key: Process, data: N/A, inputType: N/A, isComposing: true
keyup - key: s, data: N/A, inputType: N/A, isComposing: true
keydown - key: Process, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 가, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
compositionend - key: N/A, data: 가, inputType: N/A, isComposing: N/A
compositionstart - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
compositionupdate - key: N/A, data: 나, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 나, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 나, inputType: insertCompositionText, isComposing: true
keyup - key: Process, data: N/A, inputType: N/A, isComposing: true
keyup - key: k, data: N/A, inputType: N/A, isComposing: true
keydown - key: Process, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 낟, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 낟, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 낟, inputType: insertCompositionText, isComposing: true
keyup - key: Process, data: N/A, inputType: N/A, isComposing: true
keyup - key: e, data: N/A, inputType: N/A, isComposing: true
keydown - key: Process, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 나, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 나, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 나, inputType: insertCompositionText, isComposing: true
compositionend - key: N/A, data: 나, inputType: N/A, isComposing: N/A
compositionstart - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
compositionupdate - key: N/A, data: 다, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 다, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 다, inputType: insertCompositionText, isComposing: true
keyup - key: Process, data: N/A, inputType: N/A, isComposing: true
keyup - key: k, data: N/A, inputType: N/A, isComposing: true
compositionend - key: N/A, data: 다, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 다, inputType: insertText, isComposing: N/A
input - key: N/A, data: 다, inputType: insertText, isComposing: N/A
Ubuntu 24.04
keydown - key: Process, data: N/A, inputType: N/A, isComposing: N/A
compositionstart - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
compositionupdate - key: N/A, data: ㄱ, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: ㄱ, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: ㄱ, inputType: insertCompositionText, isComposing: true
keyup - key: r, data: N/A, inputType: N/A, isComposing: true
keydown - key: Process, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 가, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
keyup - key: k, data: N/A, inputType: N/A, isComposing: true
keydown - key: Process, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 간, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 간, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 간, inputType: insertCompositionText, isComposing: true
keyup - key: s, data: N/A, inputType: N/A, isComposing: true
keydown - key: Process, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: N/A, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: N/A, inputType: insertCompositionText, isComposing: true
compositionend - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
input - key: N/A, data: N/A, inputType: insertCompositionText, isComposing: N/A
beforeinput - key: N/A, data: 가, inputType: insertText, isComposing: N/A
input - key: N/A, data: 가, inputType: insertText, isComposing: N/A
compositionstart - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
compositionupdate - key: N/A, data: 나, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 나, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 나, inputType: insertCompositionText, isComposing: true
keyup - key: k, data: N/A, inputType: N/A, isComposing: true
keydown - key: Process, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 낟, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 낟, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 낟, inputType: insertCompositionText, isComposing: true
keyup - key: e, data: N/A, inputType: N/A, isComposing: true
keydown - key: Process, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: N/A, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: N/A, inputType: insertCompositionText, isComposing: true
compositionend - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
input - key: N/A, data: N/A, inputType: insertCompositionText, isComposing: N/A
beforeinput - key: N/A, data: 나, inputType: insertText, isComposing: N/A
input - key: N/A, data: 나, inputType: insertText, isComposing: N/A
compositionstart - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
compositionupdate - key: N/A, data: 다, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 다, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 다, inputType: insertCompositionText, isComposing: true
keyup - key: k, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: N/A, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: N/A, inputType: insertCompositionText, isComposing: true
compositionupdate - key: N/A, data: 다, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 다, inputType: insertCompositionText, isComposing: true
compositionend - key: N/A, data: 다, inputType: N/A, isComposing: N/A
input - key: N/A, data: 다, inputType: insertCompositionText, isComposing: N/A
macOS 15.4
keydown - key: ㄱ, data: N/A, inputType: N/A, isComposing: N/A
compositionstart - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
compositionupdate - key: N/A, data: ㄱ, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: ㄱ, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: ㄱ, inputType: insertCompositionText, isComposing: true
keyup - key: ㄱ, data: N/A, inputType: N/A, isComposing: true
keydown - key: ㅏ, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 가, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
keyup - key: ㅏ, data: N/A, inputType: N/A, isComposing: true
keydown - key: ㄴ, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 간, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 간, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 간, inputType: insertCompositionText, isComposing: true
keyup - key: ㄴ, data: N/A, inputType: N/A, isComposing: true
keydown - key: ㅏ, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 가, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 가, inputType: insertCompositionText, isComposing: true
compositionend - key: N/A, data: 가, inputType: N/A, isComposing: N/A
compositionstart - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
compositionupdate - key: N/A, data: 나, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 나, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 나, inputType: insertCompositionText, isComposing: true
keyup - key: ㅏ, data: N/A, inputType: N/A, isComposing: true
keydown - key: ㄷ, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 낟, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 낟, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 낟, inputType: insertCompositionText, isComposing: true
keyup - key: ㄷ, data: N/A, inputType: N/A, isComposing: true
keydown - key: ㅏ, data: N/A, inputType: N/A, isComposing: true
compositionupdate - key: N/A, data: 나, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 나, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 나, inputType: insertCompositionText, isComposing: true
compositionend - key: N/A, data: 나, inputType: N/A, isComposing: N/A
compositionstart - key: N/A, data: N/A, inputType: N/A, isComposing: N/A
compositionupdate - key: N/A, data: 다, inputType: N/A, isComposing: N/A
beforeinput - key: N/A, data: 다, inputType: insertCompositionText, isComposing: true
input - key: N/A, data: 다, inputType: insertCompositionText, isComposing: true
keyup - key: ㅏ, data: N/A, inputType: N/A, isComposing: true
compositionend - key: N/A, data: 다, inputType: N/A, isComposing: N/A
Explanation of issues:
iOS default keyboard
Because we clear the input for each character typed, the characters do not combine, as the only input event that fires is an insertText for each individual character (all six) and there are no composition events or advanced text events. We'd need to properly handle the advanced text events anyway.iOS GBoard
Unknown issue as I don't know what events it fires. Probably relates at least partially to clearing the input for each character typed.Android Samsung Keyboard
No issue, every input event occurs during composition and the data at compositionend is correct, containing all the entered text.Android GBoard
For the initially mentioned input, all input events happen during composition and the data at compositionend is correct. I'd need to see the events that fire when entering 1 + 2 + 3 + ㄱ + ㅏ + ㄴ + ㅏ + ㄷ + ㅏ to know the issue there.Windows default keyboard
Because we clear the input for each character typed, the deleteContentBackward event that is supposed to fire before a final input event duplicates the last combined character does not fire. Even if it did, we don't handle deleteContentBackward.macOS default keyboard
Unsure, based on the listed events I would expect everything to work correctly. If you ignore the order, macOS seems to be typing everything twice.Linux default keyboard
No issue, there is a non-composing insertText input event containing 가 that occurs following a compositionend event with no data, a non-composing insertText input event containing 나 that occurs following a compositionend event with no data, and a compositionend event at the end containing 다 as data.
I'm about 99.9% sure we need to stop clearing the hidden input for each character typed in the future.
I have a theory for what Mac is doing wrong, and if I'm right I wonder if this would help:
this.virtualKeyboard.addEventListener("keydown", this.ignoreComposingKeyEvents.bind(this));
this.virtualKeyboard.addEventListener("keyup", this.ignoreComposingKeyEvents.bind(this));
ignoreComposingKeyEvents(event: KeyboardEvent) {
if (event.isComposing) {
event.preventDefault();
event.stopPropagation();
return;
}
}
My theory is maybe Mac attempts to fire events for composing key events, causing the keyup/keydown to write text too.
I'm about 99.9% sure we need to stop clearing the hidden input for each character typed in the future.
Me too. Moreover, I'm 97% sure we should just synchronize the text field with the HTML input and issue Flash events based on changes.
But that's a separate issue from IME, as IME in Flash behaves differently to normal input (and it gets underlined). Even with full text synchronization we should translate IME events to Flash.
Note, the tsx files weren't being linted so it's entirely possible that when this gets rebased it ends up with lint failures.
BTW I'm still trying to write a test for it, but the process is tedious. I'll try my best to include it here