mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-11 14:21:52 +09:00
first hangul ime
This commit is contained in:
377
assets/keylayout/ko_kr_3set_390.ime
Normal file
377
assets/keylayout/ko_kr_3set_390.ime
Normal file
@@ -0,0 +1,377 @@
|
||||
let states = {"keylayouts":[[""],[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
["\u110F",")"],
|
||||
["\u11C2","\u11BD"],
|
||||
["\u11BB","@"],
|
||||
["\u11B8","#"],
|
||||
["\u116D","$"],
|
||||
["\u1172","%"],
|
||||
["\u1163","^"],
|
||||
["\u1168","&"],
|
||||
["\u1174","*"],
|
||||
["\u116E","("],
|
||||
["*"],
|
||||
["#"],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
["\u11BC","\u11AE"],
|
||||
["\u116E","!"],
|
||||
["\u1166","\u11B1"],
|
||||
["\u1175","\u11B0"],
|
||||
["\u1167","\u11BF"],
|
||||
["\u1161","\u11A9"],
|
||||
["\u1173","/"],
|
||||
["\u1102","'"],
|
||||
["\u1106","8"],
|
||||
["\u110B","4"],
|
||||
["\u1100","5"],
|
||||
["\u110C","6"],
|
||||
["\u1112","1"],
|
||||
["\u1109","0"],
|
||||
["\u110E","9"],
|
||||
["\u1111",">"],
|
||||
["\u11BA","\u11C1"],
|
||||
["\u1162","\u1164"],
|
||||
["\u11AB","\u11AD"],
|
||||
["\u1165",";"],
|
||||
["\u1103","7"],
|
||||
["\u1169","\u11B6"],
|
||||
["\u11AF","\u11C0"],
|
||||
["\u11A8","\u11B9"],
|
||||
["\u1105","<"],
|
||||
["\u11B7","\u11BE"],
|
||||
[",","2"],
|
||||
[".","3"],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[" "],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
["\n"],
|
||||
["\x08"],
|
||||
["`","~"],
|
||||
["-","_"],
|
||||
["=","+"],
|
||||
["[","{"],
|
||||
["]","}"],
|
||||
["\\","|"],
|
||||
["\u1107",":"],
|
||||
["\u1110",'"'],
|
||||
["\u1169","?"],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined],
|
||||
[undefined]
|
||||
],
|
||||
"code":0,
|
||||
"buf":[]}
|
||||
let reset = () => {
|
||||
states.code = 0
|
||||
states.buf = []
|
||||
}
|
||||
let inRange = (s,a,b) => (a <= s && s <= b)
|
||||
let isHangul = (s) => (s === undefined) ? false : inRange(s.charCodeAt(0), 0x1100, 0x11C2)
|
||||
let isChoseong = (s) => (s === undefined) ? false : inRange(s.charCodeAt(0), 0x1100, 0x1112)
|
||||
let isJungseong = (s) => (s === undefined) ? false : inRange(s.charCodeAt(0), 0x1161, 0x1175)
|
||||
let isJongseong = (s) => (s === undefined) ? false : inRange(s.charCodeAt(0), 0x11A8, 0x11C2)
|
||||
let isChoseongDigraph = (s) => (s === undefined) ? false : ([0x1100, 0x1103, 0x1107, 0x1109, 0x110C].includes(s.charCodeAt(0)))
|
||||
let isJungseongDigraph1 = (s) => (s === undefined) ? false : ([0x1169, 0x116E].includes(s.charCodeAt(0)))
|
||||
let isJungseongDigraphO = (s) => (s === undefined) ? false : ([0x1161, 0x1162, 0x1175].includes(s.charCodeAt(0)))
|
||||
let isJungseongDigraphU = (s) => (s === undefined) ? false : ([0x1165, 0x1166, 0x1175].includes(s.charCodeAt(0)))
|
||||
let isJungseongDigraphEU = (s) => (s === undefined) ? false : ([0x1175].includes(s.charCodeAt(0)))
|
||||
let isJongseongDigraphG = (s) => (s === undefined) ? false : ([0x11A8, 0x11BA].includes(s.charCodeAt(0)))
|
||||
let isJongseongDigraphN = (s) => (s === undefined) ? false : ([0x11BD, 0x11C2].includes(s.charCodeAt(0)))
|
||||
let isJongseongDigraphR = (s) => (s === undefined) ? false : ([0x11A8, 0x11B7, 0x11B8, 0x11BA, 0x11C0, 0x11C1, 0x11C2].includes(s.charCodeAt(0)))
|
||||
let isJongseongDigraphB = (s) => (s === undefined) ? false : ([0x11BA].includes(s.charCodeAt(0)))
|
||||
let choseongDigraphs = {"\u1100":"\u1101", "\u1103":"\u1104", "\u1107":"\u1108", "\u1109":"\u110A", "\u110C":"\u110D"}
|
||||
let jungseongDigraphsO = {"\u1161":"\u116A", "\u1162":"\u116B", "\u1175":"\u116C"}
|
||||
let jungseongDigraphsU = {"\u1165":"\u116F", "\u1166":"\u1170", "\u1175":"\u1171"}
|
||||
let jungseongDigraphsEU = {"\u1175":"\u1174"}
|
||||
let jongseongDigraphsG = {"\u11A8":"\u11A9", "\u11BA":"\u11AA"}
|
||||
let jongseongDigraphsN = {"\u11BD":"\u11AC", "\u11C2":"\u11AD"}
|
||||
let jongseongDigraphsR = {"\u11A8":"\u11B0", "\u11B7":"\u11B1", "\u11B8":"\u11B2", "\u11BA":"\u11B3", "\u11C0":"\u11B4", "\u11C1":"\u11B5", "\u11C2":"\u11B6"}
|
||||
let jongseongDigraphsB = {"\u11BA":"\u11B9"}
|
||||
let bufAssemble = () => {
|
||||
if (states.buf[0] === undefined && states.buf[1] === undefined && states.buf[2] === undefined)
|
||||
return ''
|
||||
else if (states.buf[1] === undefined && isHangul(states.buf[0]))
|
||||
return [states.buf[0], "\u1160", states.buf[2]].join('')
|
||||
else if (states.buf[0] === undefined && isHangul(states.buf[1]))
|
||||
return ["\u115F", states.buf[1], states.buf[2]].join('')
|
||||
else if (isHangul(states.buf[2]) && states.buf[0] === undefined && states.buf[1] === undefined )
|
||||
return ["\u115F", "\u1160", states.buf[2]].join('')
|
||||
else
|
||||
return states.buf.join('')
|
||||
}
|
||||
Object.freeze({"n":"세벌식 3-90","states":states,
|
||||
// return: [displayed output, composed output]
|
||||
"accept":(keycodes,shiftin,altgrin)=>{
|
||||
let layer = 1*shiftin// + 2*altgrin
|
||||
states.code = 1
|
||||
|
||||
let purekeys = keycodes.filter(it => (
|
||||
inRange(it,7,18) || // numeric
|
||||
inRange(it,29,56) || // alph
|
||||
it == 62 || // space
|
||||
inRange(it,66,76) || // symbols
|
||||
inRange(it,144,163) // numpad
|
||||
))
|
||||
let headkey = purekeys[0]
|
||||
|
||||
let s = states.keylayouts[headkey][layer]
|
||||
let bufIndex = isJungseong(s) ? 1 : isJongseong(s) ? 2 : 0
|
||||
|
||||
console.log(`IME accepting keycodes ${keycodes}`)
|
||||
|
||||
if (isHangul(s)) {
|
||||
// ㄲ ㄸ ㅃ ㅆ ㅉ
|
||||
if (0 == bufIndex && isChoseongDigraph(states.buf[0]) && isChoseongDigraph(s)) {
|
||||
states.buf[0] = choseongDigraphs[s]
|
||||
}
|
||||
// ㅘ ㅙ ㅚ
|
||||
else if (1 == bufIndex && "\u1169" == states.buf[1] && isJungseongDigraphO(s)) {
|
||||
states.buf[1] = jungseongDigraphsO[s]
|
||||
}
|
||||
// ㅝ ㅞ ㅟ
|
||||
else if (1 == bufIndex && "\u116E" == states.buf[1] && isJungseongDigraphU(s)) {
|
||||
states.buf[1] = jungseongDigraphsU[s]
|
||||
}
|
||||
// ㅢ
|
||||
else if (1 == bufIndex && "\u1173" == states.buf[1] && isJungseongDigraphEU(s)) {
|
||||
states.buf[1] = jungseongDigraphsEU[s]
|
||||
}
|
||||
// ㄲ ㄳ
|
||||
else if (2 == bufIndex && "\u11A8" == states.buf[2] && isJongseongDigraphG(s)) {
|
||||
states.buf[2] = jongseongDigraphsG[s]
|
||||
}
|
||||
// ㄵ ㄶ
|
||||
else if (2 == bufIndex && "\u11AB" == states.buf[2] && isJongseongDigraphN(s)) {
|
||||
states.buf[2] = jongseongDigraphsN[s]
|
||||
}
|
||||
// ㄺ ㄻ ㄼ ㄽ ㄾ ㄿ ㅀ
|
||||
else if (2 == bufIndex && "\u11AF" == states.buf[2] && isJongseongDigraphR(s)) {
|
||||
states.buf[2] = jongseongDigraphsR[s]
|
||||
}
|
||||
// ㅄ
|
||||
else if (2 == bufIndex && "\u11B8" == states.buf[2] && isJongseongDigraphB(s)) {
|
||||
states.buf[2] = jongseongDigraphsB[s]
|
||||
}
|
||||
// key inputs that bufIndex collides (end compose and accept incoming char as a new char state)
|
||||
else if (states.buf[bufIndex] !== undefined) {
|
||||
let sendout = bufAssemble(); reset()
|
||||
states.buf[bufIndex] = s
|
||||
console.log(`sending out: ${sendout}`)
|
||||
return [bufAssemble(), sendout]
|
||||
}
|
||||
else {
|
||||
states.buf[bufIndex] = s
|
||||
console.log(`assembling: ${bufAssemble()}`)
|
||||
}
|
||||
|
||||
return [bufAssemble(), ""]
|
||||
}
|
||||
else {
|
||||
let sendout = bufAssemble() + s; reset()
|
||||
console.log(`sending out: ${sendout}`)
|
||||
return [bufAssemble(), sendout]
|
||||
}
|
||||
},
|
||||
"end":()=>{
|
||||
console.log(`end composing`)
|
||||
reset()
|
||||
return bufAssemble()
|
||||
},
|
||||
"reset":()=>{ reset() },
|
||||
"composing":()=>(states.code!=0)
|
||||
})
|
||||
@@ -100,7 +100,8 @@ object DefaultConfig {
|
||||
"fx_differential" to false,
|
||||
//"fx_3dlut" to false,
|
||||
|
||||
"basekeyboardlayout" to "us_qwerty"
|
||||
"basekeyboardlayout" to "us_qwerty",
|
||||
"inputmethod" to "none"
|
||||
|
||||
|
||||
// settings regarding debugger
|
||||
|
||||
@@ -5,8 +5,16 @@ import java.io.File
|
||||
|
||||
data class TerrarumKeyLayout(
|
||||
val name: String,
|
||||
val symbols: Array<Array<String?>>?,
|
||||
val acceptChar: ((Int) -> String?)? = null
|
||||
val symbols: Array<Array<String?>>?
|
||||
)
|
||||
|
||||
data class TerrarumInputMethod(
|
||||
val name: String,
|
||||
// (keycodes, shiftin, altgrin)
|
||||
val acceptChar: (IntArray, Boolean, Boolean) -> Pair<String, String>, // Pair<Display Char, Output Char if any>
|
||||
val endCompose: () -> String,
|
||||
val reset: () -> Unit,
|
||||
val composing: () -> Boolean
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -26,8 +34,10 @@ object IME {
|
||||
|
||||
const val KEYLAYOUT_DIR = "assets/keylayout/"
|
||||
const val KEYLAYOUT_EXTENSION = "key"
|
||||
const val IME_EXTENSION = "ime"
|
||||
|
||||
private val lowLayers = HashMap<String, TerrarumKeyLayout>()
|
||||
private val highLayers = HashMap<String, TerrarumInputMethod>()
|
||||
|
||||
private val context = org.graalvm.polyglot.Context.newBuilder("js")
|
||||
.allowHostAccess(org.graalvm.polyglot.HostAccess.NONE)
|
||||
@@ -40,6 +50,11 @@ object IME {
|
||||
printdbg(this, "Registering Low layer ${it.nameWithoutExtension.lowercase()}")
|
||||
lowLayers[it.nameWithoutExtension.lowercase()] = parseKeylayoutFile(it)
|
||||
}
|
||||
|
||||
File(KEYLAYOUT_DIR).listFiles { file, s -> s.endsWith(".$IME_EXTENSION") }.forEach {
|
||||
printdbg(this, "Registering High layer ${it.nameWithoutExtension.lowercase()}")
|
||||
highLayers[it.nameWithoutExtension.lowercase()] = parseImeFile(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun invoke() {}
|
||||
@@ -48,15 +63,22 @@ object IME {
|
||||
return lowLayers[name.lowercase()]!!
|
||||
}
|
||||
|
||||
fun getHighLayerByName(name: String): TerrarumInputMethod {
|
||||
return highLayers[name.lowercase()]!!
|
||||
}
|
||||
|
||||
fun getAllLowLayers(): List<String> {
|
||||
return lowLayers.keys.toList()
|
||||
}
|
||||
|
||||
fun getAllHighLayers(): List<String> {
|
||||
return highLayers.keys.toList()
|
||||
}
|
||||
|
||||
|
||||
private fun parseKeylayoutFile(file: File): TerrarumKeyLayout {
|
||||
val src = file.readText(Charsets.UTF_8)
|
||||
val jsval = context.eval("js", "Object.freeze($src)")
|
||||
val jsval = context.eval("js", "'use strict';Object.freeze($src)")
|
||||
val name = jsval.getMember("n").asString()
|
||||
val out = Array(256) { Array<String?>(4) { null } }
|
||||
for (keycode in 0L until 256L) {
|
||||
@@ -78,4 +100,23 @@ object IME {
|
||||
return TerrarumKeyLayout(name, out)
|
||||
}
|
||||
|
||||
private fun parseImeFile(file: File): TerrarumInputMethod {
|
||||
val src = file.readText(Charsets.UTF_8)
|
||||
val jsval = context.eval("js", "'use strict';$src")
|
||||
val name = jsval.getMember("n").asString()
|
||||
|
||||
|
||||
return TerrarumInputMethod(name, { it, shifted, alted ->
|
||||
val a = jsval.invokeMember("accept", context.eval("js", "'${it.joinToString(",")}'.split(',')"), shifted, alted)
|
||||
a.getArrayElement(0).asString() to a.getArrayElement(1).asString()
|
||||
}, {
|
||||
jsval.invokeMember("end").asString()
|
||||
}, {
|
||||
jsval.invokeMember("reset")
|
||||
}, {
|
||||
jsval.invokeMember("composing").asBoolean()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -138,19 +138,29 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
private val lowLayerNames = lowLayerCodes.map { { IME.getLowLayerByName(it).name } }
|
||||
private val keyboardLayoutSelection = UIItemTextSelector(this, drawX + width - textSelWidth - 3, 400, lowLayerNames, lowLayerCodes.linearSearch { it == App.getConfigString("basekeyboardlayout") }!!, textSelWidth)
|
||||
|
||||
private val imeCodes = listOf("null", "ko_kr_2set_standard", "ko_kr_3set_390")
|
||||
private val imeNames = listOf({ "$EMDASH" },{ "표준 두벌식" },{ "세벌식 3-90" })
|
||||
private val imeSelection = UIItemTextSelector(this, drawX + width - textSelWidth - 3, 440, imeNames, 0, textSelWidth)
|
||||
private val imeCodes0 = IME.getAllHighLayers()
|
||||
private val imeCodes = listOf("none") + IME.getAllHighLayers()
|
||||
private val imeNames = listOf({"$EMDASH"}) + imeCodes0.map { { IME.getHighLayerByName(it).name } }
|
||||
private val imeSelection = UIItemTextSelector(this, drawX + width - textSelWidth - 3, 440, imeNames, imeCodes.linearSearch { it == App.getConfigString("inputmethod") }!!, textSelWidth)
|
||||
|
||||
|
||||
|
||||
private val keyboardTestPanel = UIItemTextLineInput(this, drawX + (width - 480) / 2 + 3, 480, 474, enableIMEButton = true, enablePasteButton = true)
|
||||
|
||||
|
||||
private val keyboardConfigItems = listOf(
|
||||
keyboardLayoutSelection,
|
||||
imeSelection,
|
||||
keyboardTestPanel
|
||||
)
|
||||
|
||||
init {
|
||||
keyboardLayoutSelection.selectionChangeListener = {
|
||||
App.setConfig("basekeyboardlayout", lowLayerCodes[it])
|
||||
}
|
||||
imeSelection.selectionChangeListener = {
|
||||
App.setConfig("inputmethod", imeCodes[it])
|
||||
}
|
||||
|
||||
keycaps.values.forEach { addUIitem(it) }
|
||||
updateKeycaps()
|
||||
@@ -161,9 +171,9 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
updateKeycaps()
|
||||
}
|
||||
|
||||
addUIitem(keyboardLayoutSelection)
|
||||
addUIitem(imeSelection)
|
||||
addUIitem(keyboardTestPanel)
|
||||
// addUIitem(keyboardLayoutSelection)
|
||||
// addUIitem(imeSelection)
|
||||
// addUIitem(keyboardTestPanel)
|
||||
}
|
||||
|
||||
private fun resetKeyConfig() {
|
||||
@@ -219,6 +229,9 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
if (keycapClicked >= 0 && controlSelected < 0) {
|
||||
controlPalette.update(delta)
|
||||
}
|
||||
else {
|
||||
keyboardConfigItems.forEach { it.update(delta) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderUI(batch: SpriteBatch, camera: Camera) {
|
||||
@@ -231,14 +244,19 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
|
||||
batch.color = Color.WHITE
|
||||
|
||||
if (keycapClicked >= 0 && controlSelected < 0) {
|
||||
controlPalette.render(batch, camera)
|
||||
}
|
||||
App.fontGame.draw(batch, Lang["MENU_LABEL_KEYBOARD_LAYOUT"], kbx + 1, keyboardLayoutSelection.initialY)
|
||||
App.fontGame.draw(batch, Lang["MENU_LABEL_IME"], kbx + 1, imeSelection.initialY)
|
||||
|
||||
|
||||
if (keycapClicked >= 0 && controlSelected < 0) {
|
||||
controlPalette.render(batch, camera)
|
||||
}
|
||||
else {
|
||||
keyboardConfigItems.forEach { it.render(batch, camera) }
|
||||
}
|
||||
|
||||
val title = Lang["MENU_CONTROLS_KEYBOARD"]
|
||||
batch.color = Color.WHITE
|
||||
App.fontGame.draw(batch, title, drawX.toFloat() + (width - App.fontGame.getWidth(title)) / 2, drawY.toFloat())
|
||||
|
||||
}
|
||||
@@ -252,11 +270,19 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
|
||||
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||
buttonReset.touchDown(screenX, screenY, pointer, button)
|
||||
keyboardConfigItems.forEach { it.touchDown(screenX, screenY, pointer, button) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||
buttonReset.touchUp(screenX, screenY, pointer, button)
|
||||
keyboardConfigItems.forEach { it.touchUp(screenX, screenY, pointer, button) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun scrolled(amountX: Float, amountY: Float): Boolean {
|
||||
super.scrolled(amountX, amountY)
|
||||
keyboardConfigItems.forEach { it.scrolled(amountX, amountY) }
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,13 @@ import com.badlogic.gdx.graphics.Pixmap
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.graphics.glutils.FrameBuffer
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.gamecontroller.IME
|
||||
import net.torvald.terrarum.gamecontroller.IngameController
|
||||
import net.torvald.terrarum.gamecontroller.TerrarumInputMethod
|
||||
import net.torvald.terrarum.utils.Clipboard
|
||||
import net.torvald.terrarumsansbitmap.gdx.CodepointSequence
|
||||
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
||||
import java.util.ArrayList
|
||||
import kotlin.streams.toList
|
||||
|
||||
data class InputLenCap(val count: Int, val unit: CharLenUnit) {
|
||||
@@ -87,7 +90,11 @@ class UIItemTextLineInput(
|
||||
true
|
||||
)
|
||||
|
||||
var isActive = true
|
||||
var isActive: Boolean = true
|
||||
set(value) {
|
||||
resetIME()
|
||||
field = value
|
||||
}
|
||||
|
||||
var cursorX = 0
|
||||
var cursorDrawScroll = 0
|
||||
@@ -113,6 +120,21 @@ class UIItemTextLineInput(
|
||||
get() = buttonsShown > 0 && relativeMouseX in btn2PosX - posX until btn2PosX - posX + WIDTH_ONEBUTTON && relativeMouseY in 0 until height
|
||||
|
||||
private var imeOn = false
|
||||
private var composingView = CodepointSequence()
|
||||
|
||||
private fun getIME(): TerrarumInputMethod? {
|
||||
if (!imeOn) return null
|
||||
|
||||
val selectedIME = App.getConfigString("inputmethod")
|
||||
|
||||
if (selectedIME == "none") return null
|
||||
try {
|
||||
return IME.getHighLayerByName(selectedIME)
|
||||
}
|
||||
catch (e: NullPointerException) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun forceLitCursor() {
|
||||
cursorBlinkCounter = 0f
|
||||
@@ -139,13 +161,14 @@ class UIItemTextLineInput(
|
||||
isActive = mouseUp
|
||||
}
|
||||
|
||||
// TODO cursorDrawX kerning-aware
|
||||
if (App.getConfigString("inputmethod") == "none") imeOn = false
|
||||
|
||||
// process keypresses
|
||||
if (isActive) {
|
||||
IngameController.withKeyboardEvent { (_, char, _, keycodes) ->
|
||||
fboUpdateLatch = true
|
||||
forceLitCursor()
|
||||
val ime = getIME()
|
||||
|
||||
if (keycodes.contains(Input.Keys.V) && (keycodes.contains(Input.Keys.CONTROL_LEFT) || keycodes.contains(Input.Keys.CONTROL_RIGHT))) {
|
||||
paste()
|
||||
@@ -154,18 +177,37 @@ class UIItemTextLineInput(
|
||||
else if (keycodes.contains(Input.Keys.C) && (keycodes.contains(Input.Keys.CONTROL_LEFT) || keycodes.contains(Input.Keys.CONTROL_RIGHT))) {
|
||||
copyToClipboard()
|
||||
}
|
||||
else if (cursorX > 0 && keycodes.contains(Input.Keys.BACKSPACE)) {
|
||||
cursorX -= 1
|
||||
textbuf.removeAt(cursorX)
|
||||
cursorDrawX = App.fontGame.getWidth(textbuf.subList(0, cursorX))
|
||||
tryCursorForward()
|
||||
else if (keycodes.contains(Input.Keys.BACKSPACE)) {
|
||||
if (ime != null && composingView.size > 0) {
|
||||
resetIME()
|
||||
}
|
||||
else if (cursorX <= 0) {
|
||||
cursorX = 0
|
||||
cursorDrawX = 0
|
||||
cursorDrawScroll = 0
|
||||
}
|
||||
else {
|
||||
if (cursorX > 0) {
|
||||
while (true) {
|
||||
cursorX -= 1
|
||||
val oldCode = textbuf.removeAt(cursorX)
|
||||
// continue deleting hangul pieces because of the font...
|
||||
if (cursorX == 0 || (oldCode !in 0x115F..0x11FF && oldCode !in 0xD7B0..0xD7FF)) break
|
||||
}
|
||||
|
||||
cursorDrawX = App.fontGame.getWidth(textbuf.subList(0, cursorX))
|
||||
tryCursorForward()
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cursorX > 0 && keycodes.contains(Input.Keys.LEFT)) {
|
||||
// TODO IME endComposing()
|
||||
cursorX -= 1
|
||||
cursorDrawX = App.fontGame.getWidth(textbuf.subList(0, cursorX))
|
||||
tryCursorForward()
|
||||
}
|
||||
else if (cursorX < textbuf.size && keycodes.contains(Input.Keys.RIGHT)) {
|
||||
// TODO IME endComposing()
|
||||
cursorX += 1
|
||||
cursorDrawX = App.fontGame.getWidth(textbuf.subList(0, cursorX))
|
||||
tryCursorBack()
|
||||
@@ -174,7 +216,18 @@ class UIItemTextLineInput(
|
||||
// - literal "<"
|
||||
// - keysymbol that does not start with "<" (not always has length of 1 because UTF-16)
|
||||
else if (char != null && char[0].code >= 32 && (char == "<" || !char.startsWith("<"))) {
|
||||
val codepoints = char.toCodePoints()
|
||||
val shiftin = keycodes.contains(Input.Keys.SHIFT_LEFT) || keycodes.contains(Input.Keys.SHIFT_RIGHT)
|
||||
val altgrin = keycodes.contains(Input.Keys.ALT_RIGHT)
|
||||
|
||||
val codepoints = if (ime != null) {
|
||||
val newStatus = ime.acceptChar(keycodes, shiftin, altgrin)
|
||||
composingView = CodepointSequence(newStatus.first.toCodePoints())
|
||||
|
||||
newStatus.second.toCodePoints()
|
||||
}
|
||||
else char.toCodePoints()
|
||||
|
||||
println("textinput codepoints: ${codepoints.map { it.toString(16) }.joinToString()}")
|
||||
|
||||
if (!maxLen.exceeds(textbuf, codepoints)) {
|
||||
textbuf.addAll(cursorX, codepoints)
|
||||
@@ -191,7 +244,7 @@ class UIItemTextLineInput(
|
||||
}
|
||||
|
||||
if (textbuf.size == 0) {
|
||||
currentPlaceholderText = ArrayList(placeholder().toCodePoints())
|
||||
currentPlaceholderText = CodepointSequence(placeholder().toCodePoints())
|
||||
}
|
||||
|
||||
cursorBlinkCounter += delta
|
||||
@@ -214,13 +267,27 @@ class UIItemTextLineInput(
|
||||
if (!mouseDown) mouseLatched = false
|
||||
}
|
||||
|
||||
private fun String.toCodePoints() = this.codePoints().toList()
|
||||
private fun String.toCodePoints() = this.codePoints().toList().filter { it > 0 }
|
||||
|
||||
private fun toggleIME() {
|
||||
if (App.getConfigString("inputmethod") == "none") {
|
||||
imeOn = false
|
||||
return
|
||||
}
|
||||
|
||||
imeOn = !imeOn
|
||||
|
||||
resetIME()
|
||||
}
|
||||
|
||||
private fun resetIME() {
|
||||
getIME()?.reset?.invoke()
|
||||
composingView = CodepointSequence()
|
||||
}
|
||||
|
||||
private fun paste() {
|
||||
resetIME()
|
||||
|
||||
val codepoints = Clipboard.fetch().substringBefore('\n').substringBefore('\t').toCodePoints()
|
||||
|
||||
val actuallyInserted = arrayListOf(0)
|
||||
@@ -306,20 +373,20 @@ class UIItemTextLineInput(
|
||||
}
|
||||
|
||||
|
||||
|
||||
// draw text
|
||||
batch.color = if (textbuf.isEmpty()) TEXTINPUT_COL_TEXT_DISABLED else TEXTINPUT_COL_TEXT
|
||||
batch.draw(fbo.colorBufferTexture, posX + 2f, posY + 2f, fbo.width.toFloat(), fbo.height.toFloat())
|
||||
|
||||
// draw text cursor
|
||||
val cursorXOnScreen = posX - cursorDrawScroll + cursorDrawX + 3
|
||||
if (isActive && cursorOn) {
|
||||
val baseCol = if (maxLen.exceeds(textbuf, listOf(32))) TEXTINPUT_COL_TEXT_NOMORE else TEXTINPUT_COL_TEXT
|
||||
|
||||
batch.color = baseCol.cpy().mul(0.5f,0.5f,0.5f,1f)
|
||||
Toolkit.fillArea(batch, posX - cursorDrawScroll + cursorDrawX + 3, posY, 2, 24)
|
||||
Toolkit.fillArea(batch, cursorXOnScreen, posY, 2, 24)
|
||||
|
||||
batch.color = baseCol
|
||||
Toolkit.fillArea(batch, posX - cursorDrawScroll + cursorDrawX + 3, posY, 1, 23)
|
||||
Toolkit.fillArea(batch, cursorXOnScreen, posY, 1, 23)
|
||||
}
|
||||
|
||||
// draw icon
|
||||
@@ -343,6 +410,19 @@ class UIItemTextLineInput(
|
||||
}
|
||||
|
||||
|
||||
// compose view background
|
||||
if (composingView.size > 0) {
|
||||
val previewTextWidth = App.fontGame.getWidth(composingView)
|
||||
val previewWindowWidth = previewTextWidth.coerceAtLeast(20)
|
||||
batch.color = TEXTINPUT_COL_BACKGROUND
|
||||
Toolkit.fillArea(batch, cursorXOnScreen + 2, posY + 27, previewWindowWidth, 20)
|
||||
// compose view border
|
||||
batch.color = Toolkit.Theme.COL_ACTIVE
|
||||
Toolkit.drawBoxBorder(batch, cursorXOnScreen + 1, posY + 26, previewWindowWidth + 2, 22)
|
||||
// compose view text
|
||||
App.fontGame.draw(batch, composingView, cursorXOnScreen + 2 + (previewWindowWidth - previewTextWidth) / 2, posY + 27)
|
||||
}
|
||||
|
||||
super.render(batch, camera)
|
||||
}
|
||||
|
||||
@@ -353,5 +433,13 @@ class UIItemTextLineInput(
|
||||
fbo.dispose()
|
||||
}
|
||||
|
||||
/*private fun CodepointSequence.toJavaString(): String {
|
||||
val sb = StringBuilder()
|
||||
this.forEach {
|
||||
sb.append(Character.toChars(it))
|
||||
}
|
||||
return sb.toString()
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user