diff --git a/assets/keylayout/ko_kr_2set_ksx5002.ime b/assets/keylayout/ko_kr_2set_ksx5002.ime index 4f30193d7..99cd9ea3b 100644 --- a/assets/keylayout/ko_kr_2set_ksx5002.ime +++ b/assets/keylayout/ko_kr_2set_ksx5002.ime @@ -343,7 +343,7 @@ let bufAssemble = (isPreview) => { } //let bufDebugStringify = (buf) => [0,1,2].map(i => (buf[i] == undefined) ? "·" : `\\u${buf[i].codePointAt(0).toString(16).toUpperCase()}`).join(' ') let bufDebugStringify = (buf) => [0,1,2].map(i => (buf[i] == undefined) ? "·" : `${buf[i]}`).join(' ') -return Object.freeze({"n":"두벌식 표준","states":states,"c":"CuriousTo\uA75Bvald", +return Object.freeze({"n":"두벌식 표준","v":"one","c":"CuriousTo\uA75Bvald", // return: [displayed output, composed output] "accept":(headkey,shiftin,altgrin)=>{ let layer = 1*shiftin// + 2*altgrin @@ -472,6 +472,5 @@ return Object.freeze({"n":"두벌식 표준","states":states,"c":"CuriousTo\uA75 return ret }, "reset":()=>{ reset() }, -"composing":()=>(states.code!=0), -"maxCandidates":()=>1 +"composing":()=>(states.code!=0) }) \ No newline at end of file diff --git a/assets/keylayout/ko_kr_3set_390.ime b/assets/keylayout/ko_kr_3set_390.ime index e73341bad..6cc429e8f 100644 --- a/assets/keylayout/ko_kr_3set_390.ime +++ b/assets/keylayout/ko_kr_3set_390.ime @@ -369,7 +369,7 @@ let bufAssemble = (isPreview) => { return states.buf.join('') } let bufDebugStringify = (buf) => [0,1,2].map(i => (buf[i] == undefined) ? "·" : `\\u${buf[i].codePointAt(0).toString(16).toUpperCase()}`).join(' ') -return Object.freeze({"n":"세벌식 3-90","states":states,"c":"CuriousTo\uA75Bvald", +return Object.freeze({"n":"세벌식 3-90","v":"one","c":"CuriousTo\uA75Bvald", // return: [displayed output, composed output] "accept":(headkey,shiftin,altgrin)=>{ let layer = 1*shiftin// + 2*altgrin @@ -474,6 +474,5 @@ return Object.freeze({"n":"세벌식 3-90","states":states,"c":"CuriousTo\uA75Bv return ret }, "reset":()=>{ reset() }, -"composing":()=>(states.code!=0), -"maxCandidates":()=>1 +"composing":()=>(states.code!=0) }) \ No newline at end of file diff --git a/assets/keylayout/ko_kr_3set_shin_p2.ime b/assets/keylayout/ko_kr_3set_shin_p2.ime index 42376d2a6..a776156ca 100644 --- a/assets/keylayout/ko_kr_3set_shin_p2.ime +++ b/assets/keylayout/ko_kr_3set_shin_p2.ime @@ -380,7 +380,7 @@ let bufAssemble = (isPreview) => { return states.buf.join('') } let bufDebugStringify = (buf) => [0,1,2].map(i => (buf[i] == undefined) ? "·" : `\\u${buf[i].codePointAt(0).toString(16).toUpperCase()}`).join(' ') -return Object.freeze({"n":"신세벌식 P2","states":states,"c":"CuriousTo\uA75Bvald", +return Object.freeze({"n":"신세벌식 P2","v":"one","c":"CuriousTo\uA75Bvald", // return: [displayed output, composed output] "accept":(headkey,shiftin,altgrin)=>{ let layer = 1*shiftin// + 2*altgrin @@ -509,6 +509,5 @@ return Object.freeze({"n":"신세벌식 P2","states":states,"c":"CuriousTo\uA75B return ret }, "reset":()=>{ reset() }, -"composing":()=>(states.code!=0), -"maxCandidates":()=>1 +"composing":()=>(states.code!=0) }) \ No newline at end of file diff --git a/assets/keylayout/zh_cn_cangjie5.ime b/assets/keylayout/zh_cn_cangjie5.ime index 2d63c67b0..3395659a8 100644 --- a/assets/keylayout/zh_cn_cangjie5.ime +++ b/assets/keylayout/zh_cn_cangjie5.ime @@ -269,7 +269,7 @@ let getCandidatesUsingBuf = () => { // console.log(`cangjie in, buf: ${states.buf}, candidates: ${states.candidates}`) return `${states.buf},${states.candidates}` } -return Object.freeze({"n":"五仓简体 Qwerty","states":states,"c":"CuriousTo\uA75Bvald, 倉頡之友 。馬來西亞 http://www.chinesecj.com", +return Object.freeze({"n":"五仓简体 Qwerty","v":"many","c":"CuriousTo\uA75Bvald, 倉頡之友 。馬來西亞 http://www.chinesecj.com", // return: [displayed output, composed output] "accept":(headkey,shiftin,altgrin)=>{ let layer = 1*shiftin// + 2*altgrin @@ -313,6 +313,5 @@ return Object.freeze({"n":"五仓简体 Qwerty","states":states,"c":"CuriousTo\u return ret }, "reset":()=>{ reset() }, -"composing":()=>(states.code!=0), -"maxCandidates":()=>10 +"composing":()=>(states.code!=0) }) \ No newline at end of file diff --git a/assets/keylayout/zh_tw_cangjie5.ime b/assets/keylayout/zh_tw_cangjie5.ime index 67659eb58..9dd148ba8 100644 --- a/assets/keylayout/zh_tw_cangjie5.ime +++ b/assets/keylayout/zh_tw_cangjie5.ime @@ -269,7 +269,7 @@ let getCandidatesUsingBuf = () => { // console.log(`cangjie in, buf: ${states.buf}, candidates: ${states.candidates}`) return `${states.buf},${states.candidates}` } -return Object.freeze({"n":"五倉正體 Qwerty","states":states,"c":"CuriousTo\uA75Bvald, 倉頡之友 。馬來西亞 http://www.chinesecj.com", +return Object.freeze({"n":"五倉正體 Qwerty","v":"many","c":"CuriousTo\uA75Bvald, 倉頡之友 。馬來西亞 http://www.chinesecj.com", // return: [displayed output, composed output] "accept":(headkey,shiftin,altgrin)=>{ let layer = 1*shiftin// + 2*altgrin @@ -313,6 +313,5 @@ return Object.freeze({"n":"五倉正體 Qwerty","states":states,"c":"CuriousTo\u return ret }, "reset":()=>{ reset() }, -"composing":()=>(states.code!=0), -"maxCandidates":()=>10 +"composing":()=>(states.code!=0) }) \ No newline at end of file diff --git a/src/net/torvald/terrarum/gamecontroller/IME.kt b/src/net/torvald/terrarum/gamecontroller/IME.kt index f2a7d10cf..b5f385e6c 100644 --- a/src/net/torvald/terrarum/gamecontroller/IME.kt +++ b/src/net/torvald/terrarum/gamecontroller/IME.kt @@ -11,17 +11,33 @@ data class TerrarumKeyLayout( val symbols: Array>? ) -data class TerrarumInputMethod( +data class TerrarumIME( val name: String, + val config: TerrarumIMEConf, // (headkey, shiftin, altgrin, lowLayerKeysym) val acceptChar: (Int, Boolean, Boolean, String) -> Pair, val backspace: () -> IMECanditates, val endCompose: () -> IMEOutput, val reset: () -> Unit, - val composing: () -> Boolean, - val maxCandidates: () -> Int + val composing: () -> Boolean ) +data class TerrarumIMEConf( + val name: String, + val copying: String, + val candidates: TerrarumIMEViewCount +) + +enum class TerrarumIMEViewCount { + NONE, ONE, MANY; + + fun toInt() = when (this) { + NONE -> 0 + ONE -> 1 + MANY -> 10 // an hard-coded config + } +} + /** * Key Layout File Structure for Low Layer: * - n: Displayed name of the keyboard layout @@ -44,7 +60,7 @@ object IME { const val IME_EXTENSION = "ime" private val lowLayers = HashMap() - private val highLayers = HashMap() + private val highLayers = HashMap() private val context = org.graalvm.polyglot.Context.newBuilder("js") .allowHostAccess(org.graalvm.polyglot.HostAccess.EXPLICIT) @@ -73,7 +89,7 @@ object IME { return lowLayers[name.lowercase()]!! } - fun getHighLayerByName(name: String): TerrarumInputMethod { + fun getHighLayerByName(name: String): TerrarumIME { return highLayers[name.lowercase()]!! } @@ -85,12 +101,20 @@ object IME { return highLayers.keys.toList() } + private fun String.toViewCount() = when (this.lowercase()) { + "none" -> TerrarumIMEViewCount.NONE + "one" -> TerrarumIMEViewCount.ONE + "many" -> TerrarumIMEViewCount.MANY + else -> throw IllegalArgumentException(this) + } private fun parseKeylayoutFile(file: File): TerrarumKeyLayout { val src = file.readText(Charsets.UTF_8) val jsval = context.eval("js", "'use strict';Object.freeze($src)") val name = jsval.getMember("n").asString() + val out = Array(256) { Array(4) { null } } + for (keycode in 0L until 256L) { val a = jsval.getMember("t").getArrayElement(keycode) if (!a.isNull) { @@ -113,26 +137,30 @@ object IME { private fun String.toCanditates(): List = this.split(',').mapNotNull { it.ifBlank { null } } - private fun parseImeFile(file: File): TerrarumInputMethod { + private fun parseImeFile(file: File): TerrarumIME { val code = file.readText(Charsets.UTF_8) val jsval = context.eval("js", "\"use strict\";(function(){$code})()") val name = jsval.getMember("n").asString() + val candidatesCount = jsval.getMember("v").asString().toViewCount() + val copying = jsval.getMember("c").asString() - - return TerrarumInputMethod(name, { headkey, shifted, alted, lowLayerKeysym -> - val a = jsval.invokeMember("accept", headkey, shifted, alted, lowLayerKeysym) - a.getArrayElement(0).asString().toCanditates() to a.getArrayElement(1).asString() - }, { - jsval.invokeMember("backspace").asString().toCanditates() - }, { - jsval.invokeMember("end").asString() - }, { - jsval.invokeMember("reset") - }, { - jsval.invokeMember("composing").asBoolean() - }, { - jsval.invokeMember("maxCandidates").asInt() - } + return TerrarumIME( + name, + TerrarumIMEConf( + name, copying, candidatesCount + ), + { headkey, shifted, alted, lowLayerKeysym -> + val a = jsval.invokeMember("accept", headkey, shifted, alted, lowLayerKeysym) + a.getArrayElement(0).asString().toCanditates() to a.getArrayElement(1).asString() + }, { + jsval.invokeMember("backspace").asString().toCanditates() + }, { + jsval.invokeMember("end").asString() + }, { + jsval.invokeMember("reset") + }, { + jsval.invokeMember("composing").asBoolean() + } ) } diff --git a/src/net/torvald/terrarum/gamecontroller/InputStrober.kt b/src/net/torvald/terrarum/gamecontroller/InputStrober.kt index cbbcfac2e..783860920 100644 --- a/src/net/torvald/terrarum/gamecontroller/InputStrober.kt +++ b/src/net/torvald/terrarum/gamecontroller/InputStrober.kt @@ -3,6 +3,7 @@ package net.torvald.terrarum.gamecontroller import com.badlogic.gdx.Gdx import com.badlogic.gdx.Input import net.torvald.terrarum.App +import net.torvald.terrarum.TerrarumAppConfiguration /** * BIG WARNING SIGN: since the strober will run on separate thread, ALWAYS BEWARE OF THE [ConcurrentModificationException]! @@ -23,9 +24,9 @@ object InputStrober { /** always Low Layer */ // private var keymap = IME.getLowLayerByName(App.getConfigString("basekeyboardlayout")) - private val thread = Thread { while (!Thread.interrupted()) { - if (Gdx.input != null) withKeyboardEvent() - } } + private val thread = Thread({ while (!Thread.interrupted()) { + try { if (Gdx.input != null) withKeyboardEvent() } catch (e: InterruptedException) { break } + } }, "${TerrarumAppConfiguration.GAME_NAME}${this.javaClass.simpleName}") init { // println("InputStrobe start") diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UILoadDemoSavefiles.kt b/src/net/torvald/terrarum/modulebasegame/ui/UILoadDemoSavefiles.kt index 953f413e2..7d5dcfabc 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UILoadDemoSavefiles.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UILoadDemoSavefiles.kt @@ -75,7 +75,7 @@ object UILoadGovernor { */ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() { - private val hash = RandomWordsName(3) +// private val hash = RandomWordsName(3) init { CommonResourcePool.addToLoadingList("inventory_category") { @@ -359,7 +359,7 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() { batch.draw(saveTex, (width - uiWidth - 10) / 2f, 0f) // draw texts - val loadGameTitleStr = Lang[titles[mode]] + "$EMDASH$hash" + val loadGameTitleStr = Lang[titles[mode]]// + "$EMDASH$hash" // "Game Load" App.fontGame.draw(batch, loadGameTitleStr, (width - App.fontGame.getWidth(loadGameTitleStr)).div(2).toFloat(), titleTextPosY.toFloat()) // Control help diff --git a/src/net/torvald/terrarum/ui/ConsoleWindow.kt b/src/net/torvald/terrarum/ui/ConsoleWindow.kt index 0da71cb1b..e869447be 100644 --- a/src/net/torvald/terrarum/ui/ConsoleWindow.kt +++ b/src/net/torvald/terrarum/ui/ConsoleWindow.kt @@ -12,6 +12,7 @@ import net.torvald.terrarum.ccE import net.torvald.terrarum.console.Authenticator import net.torvald.terrarum.console.CommandInterpreter import net.torvald.terrarum.gameactors.AVKey +import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent import net.torvald.terrarum.langpack.Lang import net.torvald.util.CircularArray @@ -32,14 +33,16 @@ class ConsoleWindow : UICanvas() { private var messageDisplayPos: Int = 0 private var messagesCount: Int = 0 - private var commandInputPool: StringBuilder? = null +// private var commandInputPool: StringBuilder? = null private var commandHistory = CircularArray(COMMAND_HISTORY_MAX, true) private val LINE_HEIGHT = 20 private val MESSAGES_DISPLAY_COUNT = 11 + private val inputToMsgboxGap = 3 + override var width: Int = App.scr.width - override var height: Int = LINE_HEIGHT * (MESSAGES_DISPLAY_COUNT + 1) + override var height: Int = LINE_HEIGHT * (MESSAGES_DISPLAY_COUNT + 1) + inputToMsgboxGap override var openCloseTime = 0f @@ -51,8 +54,12 @@ class ConsoleWindow : UICanvas() { private var iMadeTheGameToPause = false + private val textinput = UIItemTextLineInput(this, 0, 0, this.width) + init { reset() + addUIitem(textinput) + textinput.isActive = false } private val lb = ArrayList() @@ -73,6 +80,8 @@ class ConsoleWindow : UICanvas() { it.setTooltipMessage(null) } } + + uiItems.forEach { it.update(delta) } } override fun renderUI(batch: SpriteBatch, camera: Camera) { @@ -81,65 +90,75 @@ class ConsoleWindow : UICanvas() { Toolkit.fillArea(batch, drawOffX, drawOffY, width.toFloat(), height.toFloat()) Toolkit.fillArea(batch, drawOffX, drawOffY, width.toFloat(), LINE_HEIGHT.toFloat()) - val input = commandInputPool!!.toString() - val inputDrawWidth = App.fontGame.getWidth(input) - val inputDrawHeight = App.fontGame.lineHeight - - // text and cursor - batch.color = Color.WHITE - App.fontGame.draw(batch, input, 1f + drawOffX, drawOffY) - - batch.color = Color(0x7f7f7f_ff) - Toolkit.fillArea(batch, inputDrawWidth.toFloat() + drawOffX + 1, drawOffY, 2f, inputDrawHeight) - batch.color = Color.WHITE - Toolkit.fillArea(batch, inputDrawWidth.toFloat() + drawOffX + 1, drawOffY, 1f, inputDrawHeight - 1) +// val input = commandInputPool!!.toString() +// val inputDrawWidth = App.fontGame.getWidth(input) +// val inputDrawHeight = App.fontGame.lineHeight +// +// text and cursor +// batch.color = Color.WHITE +// App.fontGame.draw(batch, input, 1f + drawOffX, drawOffY) +// +// batch.color = Color(0x7f7f7f_ff) +// Toolkit.fillArea(batch, inputDrawWidth.toFloat() + drawOffX + 1, drawOffY, 2f, inputDrawHeight) +// batch.color = Color.WHITE +// Toolkit.fillArea(batch, inputDrawWidth.toFloat() + drawOffX + 1, drawOffY, 1f, inputDrawHeight - 1) // messages + batch.color = Color.WHITE + for (i in 0 until MESSAGES_DISPLAY_COUNT) { val message = messages[messageDisplayPos + i] ?: "" - App.fontGame.draw(batch, message, 1f + drawOffX, (LINE_HEIGHT * (MESSAGES_DISPLAY_COUNT - i)).toFloat() + drawOffY) + App.fontGame.draw(batch, message, 1f + drawOffX, (LINE_HEIGHT * (MESSAGES_DISPLAY_COUNT - i)).toFloat() + drawOffY + inputToMsgboxGap) } + + uiItems.forEach { it.render(batch, camera) } } + override fun inputStrobed(e: TerrarumKeyboardEvent) { + uiItems.forEach { it.inputStrobed(e) } + } override fun keyDown(key: Int): Boolean { - // history - if (key == Input.Keys.UP && historyIndex < commandHistory.elemCount) - historyIndex++ - else if (key == Input.Keys.DOWN && historyIndex >= 0) - historyIndex-- - else if (key != Input.Keys.UP && key != Input.Keys.DOWN) - historyIndex = -1 + try { + val textOnBuffer = textinput.getText() - // execute - if (key == Input.Keys.ENTER && commandInputPool!!.isNotEmpty()) { - commandHistory.appendHead(commandInputPool!!.toString()) - executeCommand() - commandInputPool = StringBuilder() + // history + if (key == Input.Keys.UP && historyIndex < commandHistory.elemCount) + historyIndex++ + else if (key == Input.Keys.DOWN && historyIndex >= 0) + historyIndex-- + else if (key != Input.Keys.UP && key != Input.Keys.DOWN) + historyIndex = -1 + + // execute + if (key == Input.Keys.ENTER && textOnBuffer.isNotEmpty()) { + commandHistory.appendHead(textOnBuffer) + executeCommand(textOnBuffer) + textinput.clearText() + } + // scroll + else if (key == Input.Keys.UP || key == Input.Keys.DOWN) { + // create new stringbuilder + textinput.clearText() + if (historyIndex >= 0) // just leave blank if index is -1 + textinput.setText(commandHistory[historyIndex] ?: "") + } + // delete input +// else if (key == Input.Keys.BACKSPACE) { +// commandInputPool = StringBuilder() +// } + // message scroll up + else if (key == Input.Keys.PAGE_UP) { + setDisplayPos(-MESSAGES_DISPLAY_COUNT + 1) + } + // message scroll down + else if (key == Input.Keys.PAGE_DOWN) { + setDisplayPos(MESSAGES_DISPLAY_COUNT - 1) + } } - // erase last letter - else if (key == Input.Keys.BACKSPACE && commandInputPool!!.isNotEmpty()) { - commandInputPool!!.deleteCharAt(commandInputPool!!.length - 1) - } - // scroll - else if (key == Input.Keys.UP || key == Input.Keys.DOWN) { - // create new stringbuilder - commandInputPool = StringBuilder() - if (historyIndex >= 0) // just leave blank if index is -1 - commandInputPool!!.append(commandHistory[historyIndex]) - } - // delete input - else if (key == Input.Keys.BACKSPACE) { - commandInputPool = StringBuilder() - } - // message scroll up - else if (key == Input.Keys.PAGE_UP) { - setDisplayPos(-MESSAGES_DISPLAY_COUNT + 1) - } - // message scroll down - else if (key == Input.Keys.PAGE_DOWN) { - setDisplayPos(MESSAGES_DISPLAY_COUNT - 1) + catch (e: ConcurrentModificationException) { + System.err.println("[ConsoleWindow] ConcurrentModificationException") } @@ -148,7 +167,7 @@ class ConsoleWindow : UICanvas() { val acceptedChars = "1234567890-=qwfpgjluy;[]\\arstdhneio'zxcvbkm,./!@#$%^&*()_+QWFPGJLUY:{}|ARSTDHNEIO\"ZXCVBKM<>? ".toSet() - override fun keyTyped(character: Char): Boolean { + /*override fun keyTyped(character: Char): Boolean { if (character in acceptedChars) { commandInputPool!!.append(character) inputCursorPos += 1 @@ -158,14 +177,14 @@ class ConsoleWindow : UICanvas() { else { return false } - } + }*/ override fun keyUp(keycode: Int): Boolean { return false } - private fun executeCommand() { - CommandInterpreter.execute(commandInputPool!!.toString()) + private fun executeCommand(s: String) { + CommandInterpreter.execute(s) } fun sendMessage(msg: String) { @@ -200,7 +219,7 @@ class ConsoleWindow : UICanvas() { messagesCount = 0 inputCursorPos = 0 commandHistory = CircularArray(COMMAND_HISTORY_MAX, true) - commandInputPool = StringBuilder() + textinput.clearText() if (Authenticator.b()) { sendMessage("$ccE${TerrarumAppConfiguration.GAME_NAME} ${App.getVERSION_STRING()} $EMDASH ${TerrarumAppConfiguration.COPYRIGHT_DATE_NAME}") @@ -232,11 +251,13 @@ class ConsoleWindow : UICanvas() { drawOffY = MovementInterpolator.fastPullOut(openingTimeCounter.toFloat() / openCloseTime.toFloat(), 0f, -height.toFloat() )*/ + textinput.isActive = false } override fun endOpening(delta: Float) { drawOffY = 0f openingTimeCounter = 0f + textinput.isActive = true } override fun endClosing(delta: Float) { @@ -252,5 +273,6 @@ class ConsoleWindow : UICanvas() { } override fun dispose() { + uiItems.forEach { it.dispose() } } } diff --git a/src/net/torvald/terrarum/ui/UIItemTextLineInput.kt b/src/net/torvald/terrarum/ui/UIItemTextLineInput.kt index d5d1c7287..66d694f0c 100644 --- a/src/net/torvald/terrarum/ui/UIItemTextLineInput.kt +++ b/src/net/torvald/terrarum/ui/UIItemTextLineInput.kt @@ -9,7 +9,6 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.glutils.FrameBuffer import com.jme3.math.FastMath import net.torvald.terrarum.* -import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.gamecontroller.* import net.torvald.terrarum.utils.Clipboard import net.torvald.terrarumsansbitmap.gdx.CodepointSequence @@ -44,7 +43,8 @@ data class InputLenCap(val count: Int, val unit: CharLenUnit) { } /** - * Make sure `inputStrobed()` of the parentUI is up and running. + * UIItemTextLineInput does not require any GDX's input event handlers, but it does require InputStrober + * to be running and `inputStrobed()` of the parentUI is calling the same method on this. * * Protip: if there are multiple TextLineInputs on a same UI, draw bottom one first, otherwise the IME's * candidate window will be hidden by the bottom UIItem if they overlaps. @@ -126,7 +126,7 @@ class UIItemTextLineInput( private val candidatesBackCol = TEXTINPUT_COL_BACKGROUND.cpy().mul(1f,1f,1f,1.5f) private val candidateNumberStrWidth = App.fontGame.getWidth("8. ") - private fun getIME(): TerrarumInputMethod? { + private fun getIME(): TerrarumIME? { if (!imeOn) return null val selectedIME = App.getConfigString("inputmethod") @@ -165,100 +165,105 @@ class UIItemTextLineInput( val (eventType, char, headkey, repeatCount, keycodes) = e - if (eventType == InputStrober.KEY_DOWN || eventType == InputStrober.KEY_CHANGE) { - fboUpdateLatch = true - forceLitCursor() - val ime = getIME() + try { + if (eventType == InputStrober.KEY_DOWN || eventType == InputStrober.KEY_CHANGE) { + fboUpdateLatch = true + forceLitCursor() + val ime = getIME() - if (keycodes.contains(App.getConfigInt("control_key_toggleime")) && repeatCount == 1) { - toggleIME() - } - else if (keycodes.contains(Input.Keys.V) && (keycodes.contains(Input.Keys.CONTROL_LEFT) || keycodes.contains(Input.Keys.CONTROL_RIGHT))) { - endComposing() - paste(Clipboard.fetch().substringBefore('\n').substringBefore('\t').toCodePoints()) - } - else if (keycodes.contains(Input.Keys.C) && (keycodes.contains(Input.Keys.CONTROL_LEFT) || keycodes.contains(Input.Keys.CONTROL_RIGHT))) { - endComposing() - copyToClipboard() - } - else if (keycodes.contains(Input.Keys.BACKSPACE)) { - if (ime != null && ime.composing()) { - candidates = ime.backspace().map { CodepointSequence(it.toCodePoints()) } + if (keycodes.contains(App.getConfigInt("control_key_toggleime")) && repeatCount == 1) { + toggleIME() } - else if (cursorX <= 0) { - cursorX = 0 - cursorDrawX = 0 - cursorDrawScroll = 0 - } - else { + else if (keycodes.contains(Input.Keys.V) && (keycodes.contains(Input.Keys.CONTROL_LEFT) || keycodes.contains(Input.Keys.CONTROL_RIGHT))) { endComposing() - 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(CodepointSequence(textbuf.subList(0, cursorX))) - tryCursorForward() - } + paste(Clipboard.fetch().substringBefore('\n').substringBefore('\t').toCodePoints()) } - } - else if (keycodes.contains(Input.Keys.LEFT)) { - endComposing() - - if (cursorX > 0) { - cursorX -= 1 - cursorDrawX = App.fontGame.getWidth(CodepointSequence(textbuf.subList(0, cursorX))) - tryCursorForward() - if (cursorX <= 0) { + else if (keycodes.contains(Input.Keys.C) && (keycodes.contains(Input.Keys.CONTROL_LEFT) || keycodes.contains(Input.Keys.CONTROL_RIGHT))) { + endComposing() + copyToClipboard() + } + else if (keycodes.contains(Input.Keys.BACKSPACE)) { + if (ime != null && ime.composing()) { + candidates = ime.backspace().map { CodepointSequence(it.toCodePoints()) } + } + else if (cursorX <= 0) { cursorX = 0 cursorDrawX = 0 cursorDrawScroll = 0 } - } - } - else if (keycodes.contains(Input.Keys.RIGHT)) { - endComposing() + else { + endComposing() + 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 + } - if (cursorX < textbuf.size) { - cursorX += 1 - cursorDrawX = App.fontGame.getWidth(CodepointSequence(textbuf.subList(0, cursorX))) - tryCursorBack() + cursorDrawX = App.fontGame.getWidth(CodepointSequence(textbuf.subList(0, cursorX))) + tryCursorForward() + } + } } - } - // accept: - // - literal "<" - // - keysymbol that does not start with "<" (not always has length of 1 because UTF-16) - else if (char != null && char.length > 0 && char[0].code >= 32 && (char == "<" || !char.startsWith("<"))) { - val shiftin = keycodes.contains(Input.Keys.SHIFT_LEFT) || keycodes.contains(Input.Keys.SHIFT_RIGHT) - val altgrin = keycodes.contains(Input.Keys.ALT_RIGHT) + else if (keycodes.contains(Input.Keys.LEFT)) { + endComposing() - val codepoints = if (ime != null) { - val newStatus = ime.acceptChar(headkey, shiftin, altgrin, char) - candidates = newStatus.first.map { CodepointSequence(it.toCodePoints()) } - - newStatus.second.toCodePoints() + if (cursorX > 0) { + cursorX -= 1 + cursorDrawX = App.fontGame.getWidth(CodepointSequence(textbuf.subList(0, cursorX))) + tryCursorForward() + if (cursorX <= 0) { + cursorX = 0 + cursorDrawX = 0 + cursorDrawScroll = 0 + } + } } - else char.toCodePoints() + else if (keycodes.contains(Input.Keys.RIGHT)) { + endComposing() + + if (cursorX < textbuf.size) { + cursorX += 1 + cursorDrawX = App.fontGame.getWidth(CodepointSequence(textbuf.subList(0, cursorX))) + tryCursorBack() + } + } + // accept: + // - literal "<" + // - keysymbol that does not start with "<" (not always has length of 1 because UTF-16) + else if (char != null && char.length > 0 && char[0].code >= 32 && (char == "<" || !char.startsWith("<"))) { + 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(headkey, shiftin, altgrin, char) + candidates = newStatus.first.map { CodepointSequence(it.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) + if (!maxLen.exceeds(textbuf, codepoints)) { + textbuf.addAll(cursorX, codepoints) - cursorX += codepoints.size - cursorDrawX = App.fontGame.getWidth(CodepointSequence(textbuf.subList(0, cursorX))) + cursorX += codepoints.size + cursorDrawX = App.fontGame.getWidth(CodepointSequence(textbuf.subList(0, cursorX))) - tryCursorBack() + tryCursorBack() + } + } + else if (keycodes.contains(Input.Keys.ENTER) || keycodes.contains(Input.Keys.NUMPAD_ENTER)) { + endComposing() } - } - else if (keycodes.contains(Input.Keys.ENTER) || keycodes.contains(Input.Keys.NUMPAD_ENTER)) { - endComposing() - } - // don't put innards of tryCursorBack/Forward here -- you absolutely don't want that behaviour + // don't put innards of tryCursorBack/Forward here -- you absolutely don't want that behaviour + } + } + catch (e: NullPointerException) { + e.printStackTrace() } if (textbuf.size == 0) { @@ -454,7 +459,7 @@ class UIItemTextLineInput( // draw candidates view if (candidates.isNotEmpty()) { val textWidths = candidates.map { App.fontGame.getWidth(CodepointSequence(it)) } - val candidatesMax = getIME()!!.maxCandidates() + val candidatesMax = getIME()!!.config.candidates.toInt() val candidatesCount = minOf(candidatesMax, candidates.size) val isOnecolumn = (candidatesCount <= 3) val halfcount = if (isOnecolumn) candidatesCount else FastMath.ceil(candidatesCount / 2f) @@ -510,6 +515,17 @@ class UIItemTextLineInput( fun getText() = textbufToString() fun getTextOrPlaceholder(): String = if (textbuf.isEmpty()) currentPlaceholderText.toJavaString() else getText() + fun clearText() { + resetIME() + textbuf.clear() + cursorX = 0 + cursorDrawScroll = 0 + cursorDrawX = 0 + } + fun setText(s: String) { + clearText() + textbuf.addAll(s.toCodePoints()) + } override fun dispose() { fbo.dispose()