Skip to content

Commit 24c6a4b

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 24c6a4b

File tree

2 files changed

+69
-6
lines changed

2 files changed

+69
-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

+34-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,39 @@ 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+
.skip(index)
472+
.next()
473+
.map(|(i, _)| i)
474+
.unwrap_or_else(|| text.len())
475+
}
476+
477+
let cursor = match (cursor_from, cursor_to) {
478+
(Some(from), Some(to)) => Some((
479+
char_index_to_byte_index(&text, from),
480+
char_index_to_byte_index(&text, to),
481+
)),
482+
_ => None,
483+
};
484+
485+
let _ = self.with_core_mut(|core| {
486+
core.handle_event(PlayerEvent::Ime(ImeEvent::Preedit(text, cursor)));
487+
});
488+
}
489+
490+
pub fn handle_ime_commit(&self, text: String) {
491+
let _ = self.with_core_mut(|core| {
492+
core.handle_event(PlayerEvent::Ime(ImeEvent::Commit(text)));
493+
});
494+
}
462495
}
463496

464497
impl RuffleHandle {

0 commit comments

Comments
 (0)