Skip to content

Commit 16005bc

Browse files
committed
web: Implement basic IME
This patch implements IME preediting and committing on web. It does not implement moving the cursor and proper positioning yet.
1 parent c6b0fb5 commit 16005bc

File tree

2 files changed

+68
-6
lines changed

2 files changed

+68
-6
lines changed

web/packages/core/src/internal/player/inner.tsx

+35-5
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,14 @@ export class InnerPlayer {
229229
"input",
230230
this.virtualKeyboardInput.bind(this),
231231
);
232+
this.virtualKeyboard.addEventListener(
233+
"compositionupdate",
234+
this.virtualKeyboardCompositionUpdate.bind(this),
235+
);
236+
this.virtualKeyboard.addEventListener(
237+
"compositionend",
238+
this.virtualKeyboardCompositionEnd.bind(this),
239+
);
232240
this.saveManager = this.shadow.getElementById(
233241
"save-manager",
234242
)! as HTMLDivElement;
@@ -1252,10 +1260,15 @@ export class InnerPlayer {
12521260
}
12531261
}
12541262

1255-
private virtualKeyboardInput() {
1256-
const input = this.virtualKeyboard;
1257-
const string = input.value;
1258-
for (const char of string) {
1263+
private virtualKeyboardInput(e: Event) {
1264+
const event = e as InputEvent;
1265+
if (!event || event.isComposing || event.inputType === 'insertCompositionText') {
1266+
// Ignore composing events, we'll get the composed text at the end
1267+
return;
1268+
}
1269+
1270+
const text = event.data || "";
1271+
for (const char of text) {
12591272
for (const eventType of ["keydown", "keyup"]) {
12601273
this.element.dispatchEvent(
12611274
new KeyboardEvent(eventType, {
@@ -1265,7 +1278,24 @@ export class InnerPlayer {
12651278
);
12661279
}
12671280
}
1268-
input.value = "";
1281+
this.virtualKeyboard.value = "";
1282+
}
1283+
1284+
private virtualKeyboardCompositionUpdate(e: Event) {
1285+
const event = e as CompositionEvent;
1286+
// TODO Add support for moving cursor during IME,
1287+
// we cannot use selectionStart & selectionEnd here,
1288+
// as they lag behind and don't take into account
1289+
// moving the caret with arrows.
1290+
const text = event.data || "";
1291+
this.instance?.handle_ime_preedit(text, text.length, text.length);
1292+
}
1293+
1294+
private virtualKeyboardCompositionEnd(e: Event) {
1295+
const event = e as CompositionEvent;
1296+
this.instance?.handle_ime_preedit("", 0, 0);
1297+
this.instance?.handle_ime_commit(event.data || "");
1298+
this.virtualKeyboard.value = "";
12691299
}
12701300

12711301
protected openVirtualKeyboard(): void {

web/src/lib.rs

+33-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use input::{web_input_to_ruffle_key_descriptor, web_to_ruffle_text_control};
1818
use js_sys::{Error as JsError, Uint8Array};
1919
use ruffle_core::context::UpdateContext;
2020
use ruffle_core::context_menu::ContextMenuCallback;
21-
use ruffle_core::events::{GamepadButton, MouseButton, MouseWheelDelta, TextControlCode};
21+
use ruffle_core::events::{GamepadButton, ImeEvent, MouseButton, MouseWheelDelta, TextControlCode};
2222
use ruffle_core::tag_utils::SwfMovie;
2323
use ruffle_core::{Player, PlayerEvent, StaticCallstack, ViewportDimensions};
2424
use ruffle_web_common::JsResult;
@@ -459,6 +459,38 @@ impl RuffleHandle {
459459
pub fn is_wasm_simd_used() -> bool {
460460
cfg!(target_feature = "simd128")
461461
}
462+
463+
pub fn handle_ime_preedit(
464+
&self,
465+
text: String,
466+
cursor_from: Option<usize>,
467+
cursor_to: Option<usize>,
468+
) {
469+
fn char_index_to_byte_index(text: &str, index: usize) -> usize {
470+
text.char_indices()
471+
.nth(index)
472+
.map(|(i, _)| i)
473+
.unwrap_or_else(|| text.len())
474+
}
475+
476+
let cursor = match (cursor_from, cursor_to) {
477+
(Some(from), Some(to)) => Some((
478+
char_index_to_byte_index(&text, from),
479+
char_index_to_byte_index(&text, to),
480+
)),
481+
_ => None,
482+
};
483+
484+
let _ = self.with_core_mut(|core| {
485+
core.handle_event(PlayerEvent::Ime(ImeEvent::Preedit(text, cursor)));
486+
});
487+
}
488+
489+
pub fn handle_ime_commit(&self, text: String) {
490+
let _ = self.with_core_mut(|core| {
491+
core.handle_event(PlayerEvent::Ime(ImeEvent::Commit(text)));
492+
});
493+
}
462494
}
463495

464496
impl RuffleHandle {

0 commit comments

Comments
 (0)