mirror of
https://github.com/godotengine/godot.git
synced 2025-10-31 21:51:22 +00:00
[Web, 3.x] Add IME input support.
This commit is contained in:
parent
2a347ab867
commit
bbe8a66a2d
4 changed files with 314 additions and 15 deletions
|
|
@ -28,6 +28,119 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
/*
|
||||
* IME API helper.
|
||||
*/
|
||||
|
||||
const GodotIME = {
|
||||
$GodotIME__deps: ['$GodotRuntime', '$GodotEventListeners'],
|
||||
$GodotIME__postset: 'GodotOS.atexit(function(resolve, reject) { GodotIME.clear(); resolve(); });',
|
||||
$GodotIME: {
|
||||
ime: null,
|
||||
active: false,
|
||||
|
||||
getModifiers: function (evt) {
|
||||
return (evt.shiftKey + 0) + ((evt.altKey + 0) << 1) + ((evt.ctrlKey + 0) << 2) + ((evt.metaKey + 0) << 3);
|
||||
},
|
||||
|
||||
ime_active: function (active) {
|
||||
function focus_timer() {
|
||||
GodotIME.active = true;
|
||||
GodotIME.ime.focus();
|
||||
}
|
||||
|
||||
if (GodotIME.ime) {
|
||||
if (active) {
|
||||
GodotIME.ime.style.display = 'block';
|
||||
setInterval(focus_timer, 100);
|
||||
} else {
|
||||
GodotIME.ime.style.display = 'none';
|
||||
GodotConfig.canvas.focus();
|
||||
GodotIME.active = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ime_position: function (x, y) {
|
||||
if (GodotIME.ime) {
|
||||
const canvas = GodotConfig.canvas;
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const rw = canvas.width / rect.width;
|
||||
const rh = canvas.height / rect.height;
|
||||
const clx = (x / rw) + rect.x;
|
||||
const cly = (y / rh) + rect.y;
|
||||
|
||||
GodotIME.ime.style.left = `${clx}px`;
|
||||
GodotIME.ime.style.top = `${cly}px`;
|
||||
}
|
||||
},
|
||||
|
||||
init: function (ime_cb, key_cb, code, key) {
|
||||
function key_event_cb(pressed, evt) {
|
||||
const modifiers = GodotIME.getModifiers(evt);
|
||||
GodotRuntime.stringToHeap(evt.code, code, 32);
|
||||
GodotRuntime.stringToHeap(evt.key, key, 32);
|
||||
key_cb(pressed, evt.repeat, modifiers);
|
||||
evt.preventDefault();
|
||||
}
|
||||
function ime_event_cb(event) {
|
||||
if (GodotIME.ime) {
|
||||
if (event.type === 'compositionstart') {
|
||||
ime_cb(0, null);
|
||||
GodotIME.ime.innerHTML = '';
|
||||
} else if (event.type === 'compositionupdate') {
|
||||
const ptr = GodotRuntime.allocString(event.data);
|
||||
ime_cb(1, ptr);
|
||||
GodotRuntime.free(ptr);
|
||||
} else if (event.type === 'compositionend') {
|
||||
const ptr = GodotRuntime.allocString(event.data);
|
||||
ime_cb(2, ptr);
|
||||
GodotRuntime.free(ptr);
|
||||
GodotIME.ime.innerHTML = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ime = document.createElement('div');
|
||||
ime.className = 'ime';
|
||||
ime.style.background = 'none';
|
||||
ime.style.opacity = 0.0;
|
||||
ime.style.position = 'fixed';
|
||||
ime.style.textAlign = 'left';
|
||||
ime.style.fontSize = '1px';
|
||||
ime.style.left = '0px';
|
||||
ime.style.top = '0px';
|
||||
ime.style.width = '100%';
|
||||
ime.style.height = '40px';
|
||||
ime.style.display = 'none';
|
||||
ime.contentEditable = 'true';
|
||||
|
||||
GodotEventListeners.add(ime, 'compositionstart', ime_event_cb, false);
|
||||
GodotEventListeners.add(ime, 'compositionupdate', ime_event_cb, false);
|
||||
GodotEventListeners.add(ime, 'compositionend', ime_event_cb, false);
|
||||
GodotEventListeners.add(ime, 'keydown', key_event_cb.bind(null, 1), false);
|
||||
GodotEventListeners.add(ime, 'keyup', key_event_cb.bind(null, 0), false);
|
||||
|
||||
ime.onblur = function () {
|
||||
this.style.display = 'none';
|
||||
GodotConfig.canvas.focus();
|
||||
GodotIME.active = false;
|
||||
};
|
||||
|
||||
GodotConfig.canvas.parentElement.appendChild(ime);
|
||||
GodotIME.ime = ime;
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
if (GodotIME.ime) {
|
||||
GodotIME.ime.remove();
|
||||
GodotIME.ime = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
mergeInto(LibraryManager.library, GodotIME);
|
||||
|
||||
/*
|
||||
* Gamepad API helper.
|
||||
*/
|
||||
|
|
@ -338,7 +451,7 @@ mergeInto(LibraryManager.library, GodotInputDragDrop);
|
|||
* Godot exposed input functions.
|
||||
*/
|
||||
const GodotInput = {
|
||||
$GodotInput__deps: ['$GodotRuntime', '$GodotConfig', '$GodotEventListeners', '$GodotInputGamepads', '$GodotInputDragDrop'],
|
||||
$GodotInput__deps: ['$GodotRuntime', '$GodotConfig', '$GodotEventListeners', '$GodotInputGamepads', '$GodotInputDragDrop', '$GodotIME'],
|
||||
$GodotInput: {
|
||||
getModifiers: function (evt) {
|
||||
return (evt.shiftKey + 0) + ((evt.altKey + 0) << 1) + ((evt.ctrlKey + 0) << 2) + ((evt.metaKey + 0) << 3);
|
||||
|
|
@ -456,6 +569,35 @@ const GodotInput = {
|
|||
GodotEventListeners.add(GodotConfig.canvas, 'keyup', key_cb.bind(null, 0), false);
|
||||
},
|
||||
|
||||
/*
|
||||
* IME API
|
||||
*/
|
||||
godot_js_set_ime_active__proxy: 'sync',
|
||||
godot_js_set_ime_active__sig: 'vi',
|
||||
godot_js_set_ime_active: function (p_active) {
|
||||
GodotIME.ime_active(p_active);
|
||||
},
|
||||
|
||||
godot_js_set_ime_position__proxy: 'sync',
|
||||
godot_js_set_ime_position__sig: 'vii',
|
||||
godot_js_set_ime_position: function (p_x, p_y) {
|
||||
GodotIME.ime_position(p_x, p_y);
|
||||
},
|
||||
|
||||
godot_js_set_ime_cb__proxy: 'sync',
|
||||
godot_js_set_ime_cb__sig: 'viiii',
|
||||
godot_js_set_ime_cb: function (p_ime_cb, p_key_cb, code, key) {
|
||||
const ime_cb = GodotRuntime.get_func(p_ime_cb);
|
||||
const key_cb = GodotRuntime.get_func(p_key_cb);
|
||||
GodotIME.init(ime_cb, key_cb, code, key);
|
||||
},
|
||||
|
||||
godot_js_is_ime_focused__proxy: 'sync',
|
||||
godot_js_is_ime_focused__sig: 'i',
|
||||
godot_js_is_ime_focused: function () {
|
||||
return GodotIME.active;
|
||||
},
|
||||
|
||||
/*
|
||||
* Gamepad API
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue