5
Keyboard Layout and IME
CuriousTorvald edited this page 2022-09-19 11:38:04 +09:00
This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Terrarum provides an IME for entering non-Latin languages.

The IME manages two keyboard layouts: the Low Layer and the High Layer. The Low Layer is usually a Latin character layout (e.g. QWERTY, Colemak) and the High Layer is more sophisticated layout for non-Latin. Changing the Low Layer does not affect the High Layer (except for the "phonetic" High Layers which will read Low Layer).

The user can switch between two layers at any moment.

The layouts are stored in <assets> or <module base dir>/keylayout/ directory and the following extensions are examined: .key for the the Low Layer and .ime for the High Layer.

The Key File (The Low Layer)

.key files are non-programmable, Javascript-based and stores the keyboard layout and some metadata.

.key files must be formatted as such:

{
    "n":"US-Intl. Qwerty", // the name of the layout
    "capslock":"caps", // the capslock mode
    "t":[ [..], [..], [..]... ] // the actual layout, 256 entries
}

of which:

  • n: the name of the keyboard layout in plaintext
  • capslock: defines the behaviour of the Caps Lock key. caps for the ordinary Caps Lock, back for the Backspace (e.g. Colemak), and shift for the Shift Lock (e.g. French language keyboard).
  • t: the array of Key Symbols, corresponds to the LibGDX keycode. Each keycode gets an array of variable length between 0 up to 4.
    • Index 0: The base layer (e.g. lowercases)
    • Index 1: The Shift layer (e.g. uppercases)
    • Index 2: The AltGr layer (e.g. symbols that require AltGr but not Shift)
    • Index 3: The AltGr-Shift layer (e.g. symbols that require both AltGr and Shift)

The Key Symbol is just a Javascript String of arbitrary length. Ones surrounded with <...> are special symbols that denotes a special key (e.g. media keys).

The Ime File (The High Layer)

.ime files are programmable variant of .key file and defines how the keyboard layout must behave when the user pushes a key.

When an IME receives a key press, it can either accept or reject the key input.

.ime files must be formatted as such:

let states = {
    "keylayouts": ..., // same format as the .key files' "t" key
    "code": 0, // used internally. Write 1 if the character is being composed, 0 if not
    "hasDeadKey": false, // if this keyboard layout has deadkeys
} // note: the states object is technically optional, but you *will* want to use it
return Object.freeze({
    "n": "두벌식 표준", // the name of the layout
    "v": "none", // candidate mode. "many" for layouts that require candidates (e.g. Chinese and Japanse), "none" if candidates are not required
    "c": "CuriousTo\uA75Bvald", // the copyright string
    "m": "rewrite", // the operation mode. Use one of "rewrite" or "candidates"
    "t": states.keylayouts.map(it => [it[0],it[1]]), // keyboard layout. Only used by the ingame config UI to show the layout info
    "l": "koKR", // the language associated with the layout
    "accept": (headkey, shiftin, altgrin) => {
        /* more codes here */
        return [arg, keysymbol]
    }, // a function that accepts a key input that returns an array
    "backspace": () => {
        /* more codes here */
        return keysymbol
    }, // a function that is called when the backspace key was down that returns a Key Symbol
    "end": () => {
        /* more codes here */
        return keysymbol
    }, // a function that is called when a composing is being cancelled that returns a Key Symbol
    "reset": () => { ... }, // a function that resets the IME's internal state
    "composing": () => { return states.code != 0 } // a function that returns `true` if the IME is in the middle of composing a character
})

The interpretation of the arg of the accept function is different for the Candidates and the Rewrite mode. On Rewrite mode, the arg is a number that indicates how many characters on the text buffer must be deleted before adding a Key Symbol; on Candidates mode, it's a "\x1E-separated" string of candidates.

Which key being a backspace depends on the Low Layer: if your Low Layer is Colemak and the Caps Lock is held while the High Layer is active, the backspace function will be invoked.

For the phonetic layouts, the t key must be replaced with tf, with an object that maps Low Layer key symbols to characters, such as:

"tf": {
    "{": "Ю", "[": "ю",
    "}": "Щ", "]": "щ",
    "A": "А", "a": "а",
    "B": "Б", "b": "б",
    "C": "Ц", "c": "ц",
    "D": "Д", "d": "д",
    /* ... */
},