js cannot be accessed concurrently

This commit is contained in:
minjaesong
2021-11-09 14:38:47 +09:00
parent 16272e76f6
commit 3c43aeec9d
10 changed files with 236 additions and 174 deletions

View File

@@ -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) ? "·" : `\\u${buf[i].codePointAt(0).toString(16).toUpperCase()}`).join(' ')
let bufDebugStringify = (buf) => [0,1,2].map(i => (buf[i] == undefined) ? "·" : `${buf[i]}`).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] // return: [displayed output, composed output]
"accept":(headkey,shiftin,altgrin)=>{ "accept":(headkey,shiftin,altgrin)=>{
let layer = 1*shiftin// + 2*altgrin let layer = 1*shiftin// + 2*altgrin
@@ -472,6 +472,5 @@ return Object.freeze({"n":"두벌식 표준","states":states,"c":"CuriousTo\uA75
return ret return ret
}, },
"reset":()=>{ reset() }, "reset":()=>{ reset() },
"composing":()=>(states.code!=0), "composing":()=>(states.code!=0)
"maxCandidates":()=>1
}) })

View File

@@ -369,7 +369,7 @@ let bufAssemble = (isPreview) => {
return states.buf.join('') return states.buf.join('')
} }
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) ? "·" : `\\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] // return: [displayed output, composed output]
"accept":(headkey,shiftin,altgrin)=>{ "accept":(headkey,shiftin,altgrin)=>{
let layer = 1*shiftin// + 2*altgrin let layer = 1*shiftin// + 2*altgrin
@@ -474,6 +474,5 @@ return Object.freeze({"n":"세벌식 3-90","states":states,"c":"CuriousTo\uA75Bv
return ret return ret
}, },
"reset":()=>{ reset() }, "reset":()=>{ reset() },
"composing":()=>(states.code!=0), "composing":()=>(states.code!=0)
"maxCandidates":()=>1
}) })

View File

@@ -380,7 +380,7 @@ let bufAssemble = (isPreview) => {
return states.buf.join('') return states.buf.join('')
} }
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) ? "·" : `\\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] // return: [displayed output, composed output]
"accept":(headkey,shiftin,altgrin)=>{ "accept":(headkey,shiftin,altgrin)=>{
let layer = 1*shiftin// + 2*altgrin let layer = 1*shiftin// + 2*altgrin
@@ -509,6 +509,5 @@ return Object.freeze({"n":"신세벌식 P2","states":states,"c":"CuriousTo\uA75B
return ret return ret
}, },
"reset":()=>{ reset() }, "reset":()=>{ reset() },
"composing":()=>(states.code!=0), "composing":()=>(states.code!=0)
"maxCandidates":()=>1
}) })

View File

@@ -269,7 +269,7 @@ let getCandidatesUsingBuf = () => {
// console.log(`cangjie in, buf: ${states.buf}, candidates: ${states.candidates}`) // console.log(`cangjie in, buf: ${states.buf}, candidates: ${states.candidates}`)
return `${states.buf},${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] // return: [displayed output, composed output]
"accept":(headkey,shiftin,altgrin)=>{ "accept":(headkey,shiftin,altgrin)=>{
let layer = 1*shiftin// + 2*altgrin let layer = 1*shiftin// + 2*altgrin
@@ -313,6 +313,5 @@ return Object.freeze({"n":"五仓简体 Qwerty","states":states,"c":"CuriousTo\u
return ret return ret
}, },
"reset":()=>{ reset() }, "reset":()=>{ reset() },
"composing":()=>(states.code!=0), "composing":()=>(states.code!=0)
"maxCandidates":()=>10
}) })

View File

@@ -269,7 +269,7 @@ let getCandidatesUsingBuf = () => {
// console.log(`cangjie in, buf: ${states.buf}, candidates: ${states.candidates}`) // console.log(`cangjie in, buf: ${states.buf}, candidates: ${states.candidates}`)
return `${states.buf},${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] // return: [displayed output, composed output]
"accept":(headkey,shiftin,altgrin)=>{ "accept":(headkey,shiftin,altgrin)=>{
let layer = 1*shiftin// + 2*altgrin let layer = 1*shiftin// + 2*altgrin
@@ -313,6 +313,5 @@ return Object.freeze({"n":"五倉正體 Qwerty","states":states,"c":"CuriousTo\u
return ret return ret
}, },
"reset":()=>{ reset() }, "reset":()=>{ reset() },
"composing":()=>(states.code!=0), "composing":()=>(states.code!=0)
"maxCandidates":()=>10
}) })

View File

@@ -11,17 +11,33 @@ data class TerrarumKeyLayout(
val symbols: Array<Array<String?>>? val symbols: Array<Array<String?>>?
) )
data class TerrarumInputMethod( data class TerrarumIME(
val name: String, val name: String,
val config: TerrarumIMEConf,
// (headkey, shiftin, altgrin, lowLayerKeysym) // (headkey, shiftin, altgrin, lowLayerKeysym)
val acceptChar: (Int, Boolean, Boolean, String) -> Pair<IMECanditates, IMEOutput>, val acceptChar: (Int, Boolean, Boolean, String) -> Pair<IMECanditates, IMEOutput>,
val backspace: () -> IMECanditates, val backspace: () -> IMECanditates,
val endCompose: () -> IMEOutput, val endCompose: () -> IMEOutput,
val reset: () -> Unit, val reset: () -> Unit,
val composing: () -> Boolean, val composing: () -> Boolean
val maxCandidates: () -> Int
) )
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: * Key Layout File Structure for Low Layer:
* - n: Displayed name of the keyboard layout * - n: Displayed name of the keyboard layout
@@ -44,7 +60,7 @@ object IME {
const val IME_EXTENSION = "ime" const val IME_EXTENSION = "ime"
private val lowLayers = HashMap<String, TerrarumKeyLayout>() private val lowLayers = HashMap<String, TerrarumKeyLayout>()
private val highLayers = HashMap<String, TerrarumInputMethod>() private val highLayers = HashMap<String, TerrarumIME>()
private val context = org.graalvm.polyglot.Context.newBuilder("js") private val context = org.graalvm.polyglot.Context.newBuilder("js")
.allowHostAccess(org.graalvm.polyglot.HostAccess.EXPLICIT) .allowHostAccess(org.graalvm.polyglot.HostAccess.EXPLICIT)
@@ -73,7 +89,7 @@ object IME {
return lowLayers[name.lowercase()]!! return lowLayers[name.lowercase()]!!
} }
fun getHighLayerByName(name: String): TerrarumInputMethod { fun getHighLayerByName(name: String): TerrarumIME {
return highLayers[name.lowercase()]!! return highLayers[name.lowercase()]!!
} }
@@ -85,12 +101,20 @@ object IME {
return highLayers.keys.toList() 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 { private fun parseKeylayoutFile(file: File): TerrarumKeyLayout {
val src = file.readText(Charsets.UTF_8) val src = file.readText(Charsets.UTF_8)
val jsval = context.eval("js", "'use strict';Object.freeze($src)") val jsval = context.eval("js", "'use strict';Object.freeze($src)")
val name = jsval.getMember("n").asString() val name = jsval.getMember("n").asString()
val out = Array(256) { Array<String?>(4) { null } } val out = Array(256) { Array<String?>(4) { null } }
for (keycode in 0L until 256L) { for (keycode in 0L until 256L) {
val a = jsval.getMember("t").getArrayElement(keycode) val a = jsval.getMember("t").getArrayElement(keycode)
if (!a.isNull) { if (!a.isNull) {
@@ -113,26 +137,30 @@ object IME {
private fun String.toCanditates(): List<String> = private fun String.toCanditates(): List<String> =
this.split(',').mapNotNull { it.ifBlank { null } } 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 code = file.readText(Charsets.UTF_8)
val jsval = context.eval("js", "\"use strict\";(function(){$code})()") val jsval = context.eval("js", "\"use strict\";(function(){$code})()")
val name = jsval.getMember("n").asString() val name = jsval.getMember("n").asString()
val candidatesCount = jsval.getMember("v").asString().toViewCount()
val copying = jsval.getMember("c").asString()
return TerrarumIME(
return TerrarumInputMethod(name, { headkey, shifted, alted, lowLayerKeysym -> name,
val a = jsval.invokeMember("accept", headkey, shifted, alted, lowLayerKeysym) TerrarumIMEConf(
a.getArrayElement(0).asString().toCanditates() to a.getArrayElement(1).asString() name, copying, candidatesCount
}, { ),
jsval.invokeMember("backspace").asString().toCanditates() { headkey, shifted, alted, lowLayerKeysym ->
}, { val a = jsval.invokeMember("accept", headkey, shifted, alted, lowLayerKeysym)
jsval.invokeMember("end").asString() a.getArrayElement(0).asString().toCanditates() to a.getArrayElement(1).asString()
}, { }, {
jsval.invokeMember("reset") jsval.invokeMember("backspace").asString().toCanditates()
}, { }, {
jsval.invokeMember("composing").asBoolean() jsval.invokeMember("end").asString()
}, { }, {
jsval.invokeMember("maxCandidates").asInt() jsval.invokeMember("reset")
} }, {
jsval.invokeMember("composing").asBoolean()
}
) )
} }

View File

@@ -3,6 +3,7 @@ package net.torvald.terrarum.gamecontroller
import com.badlogic.gdx.Gdx import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input import com.badlogic.gdx.Input
import net.torvald.terrarum.App 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]! * 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 */ /** always Low Layer */
// private var keymap = IME.getLowLayerByName(App.getConfigString("basekeyboardlayout")) // private var keymap = IME.getLowLayerByName(App.getConfigString("basekeyboardlayout"))
private val thread = Thread { while (!Thread.interrupted()) { private val thread = Thread({ while (!Thread.interrupted()) {
if (Gdx.input != null) withKeyboardEvent() try { if (Gdx.input != null) withKeyboardEvent() } catch (e: InterruptedException) { break }
} } } }, "${TerrarumAppConfiguration.GAME_NAME}${this.javaClass.simpleName}")
init { init {
// println("InputStrobe start") // println("InputStrobe start")

View File

@@ -75,7 +75,7 @@ object UILoadGovernor {
*/ */
class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() { class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() {
private val hash = RandomWordsName(3) // private val hash = RandomWordsName(3)
init { init {
CommonResourcePool.addToLoadingList("inventory_category") { CommonResourcePool.addToLoadingList("inventory_category") {
@@ -359,7 +359,7 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() {
batch.draw(saveTex, (width - uiWidth - 10) / 2f, 0f) batch.draw(saveTex, (width - uiWidth - 10) / 2f, 0f)
// draw texts // draw texts
val loadGameTitleStr = Lang[titles[mode]] + "$EMDASH$hash" val loadGameTitleStr = Lang[titles[mode]]// + "$EMDASH$hash"
// "Game Load" // "Game Load"
App.fontGame.draw(batch, loadGameTitleStr, (width - App.fontGame.getWidth(loadGameTitleStr)).div(2).toFloat(), titleTextPosY.toFloat()) App.fontGame.draw(batch, loadGameTitleStr, (width - App.fontGame.getWidth(loadGameTitleStr)).div(2).toFloat(), titleTextPosY.toFloat())
// Control help // Control help

View File

@@ -12,6 +12,7 @@ import net.torvald.terrarum.ccE
import net.torvald.terrarum.console.Authenticator import net.torvald.terrarum.console.Authenticator
import net.torvald.terrarum.console.CommandInterpreter import net.torvald.terrarum.console.CommandInterpreter
import net.torvald.terrarum.gameactors.AVKey import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.langpack.Lang
import net.torvald.util.CircularArray import net.torvald.util.CircularArray
@@ -32,14 +33,16 @@ class ConsoleWindow : UICanvas() {
private var messageDisplayPos: Int = 0 private var messageDisplayPos: Int = 0
private var messagesCount: Int = 0 private var messagesCount: Int = 0
private var commandInputPool: StringBuilder? = null // private var commandInputPool: StringBuilder? = null
private var commandHistory = CircularArray<String>(COMMAND_HISTORY_MAX, true) private var commandHistory = CircularArray<String>(COMMAND_HISTORY_MAX, true)
private val LINE_HEIGHT = 20 private val LINE_HEIGHT = 20
private val MESSAGES_DISPLAY_COUNT = 11 private val MESSAGES_DISPLAY_COUNT = 11
private val inputToMsgboxGap = 3
override var width: Int = App.scr.width 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 override var openCloseTime = 0f
@@ -51,8 +54,12 @@ class ConsoleWindow : UICanvas() {
private var iMadeTheGameToPause = false private var iMadeTheGameToPause = false
private val textinput = UIItemTextLineInput(this, 0, 0, this.width)
init { init {
reset() reset()
addUIitem(textinput)
textinput.isActive = false
} }
private val lb = ArrayList<String>() private val lb = ArrayList<String>()
@@ -73,6 +80,8 @@ class ConsoleWindow : UICanvas() {
it.setTooltipMessage(null) it.setTooltipMessage(null)
} }
} }
uiItems.forEach { it.update(delta) }
} }
override fun renderUI(batch: SpriteBatch, camera: Camera) { 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(), height.toFloat())
Toolkit.fillArea(batch, drawOffX, drawOffY, width.toFloat(), LINE_HEIGHT.toFloat()) Toolkit.fillArea(batch, drawOffX, drawOffY, width.toFloat(), LINE_HEIGHT.toFloat())
val input = commandInputPool!!.toString() // val input = commandInputPool!!.toString()
val inputDrawWidth = App.fontGame.getWidth(input) // val inputDrawWidth = App.fontGame.getWidth(input)
val inputDrawHeight = App.fontGame.lineHeight // val inputDrawHeight = App.fontGame.lineHeight
//
// text and cursor // text and cursor
batch.color = Color.WHITE // batch.color = Color.WHITE
App.fontGame.draw(batch, input, 1f + drawOffX, drawOffY) // App.fontGame.draw(batch, input, 1f + drawOffX, drawOffY)
//
batch.color = Color(0x7f7f7f_ff) // batch.color = Color(0x7f7f7f_ff)
Toolkit.fillArea(batch, inputDrawWidth.toFloat() + drawOffX + 1, drawOffY, 2f, inputDrawHeight) // Toolkit.fillArea(batch, inputDrawWidth.toFloat() + drawOffX + 1, drawOffY, 2f, inputDrawHeight)
batch.color = Color.WHITE // batch.color = Color.WHITE
Toolkit.fillArea(batch, inputDrawWidth.toFloat() + drawOffX + 1, drawOffY, 1f, inputDrawHeight - 1) // Toolkit.fillArea(batch, inputDrawWidth.toFloat() + drawOffX + 1, drawOffY, 1f, inputDrawHeight - 1)
// messages // messages
batch.color = Color.WHITE
for (i in 0 until MESSAGES_DISPLAY_COUNT) { for (i in 0 until MESSAGES_DISPLAY_COUNT) {
val message = messages[messageDisplayPos + i] ?: "" 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 { override fun keyDown(key: Int): Boolean {
// history try {
if (key == Input.Keys.UP && historyIndex < commandHistory.elemCount) val textOnBuffer = textinput.getText()
historyIndex++
else if (key == Input.Keys.DOWN && historyIndex >= 0)
historyIndex--
else if (key != Input.Keys.UP && key != Input.Keys.DOWN)
historyIndex = -1
// execute // history
if (key == Input.Keys.ENTER && commandInputPool!!.isNotEmpty()) { if (key == Input.Keys.UP && historyIndex < commandHistory.elemCount)
commandHistory.appendHead(commandInputPool!!.toString()) historyIndex++
executeCommand() else if (key == Input.Keys.DOWN && historyIndex >= 0)
commandInputPool = StringBuilder() 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 catch (e: ConcurrentModificationException) {
else if (key == Input.Keys.BACKSPACE && commandInputPool!!.isNotEmpty()) { System.err.println("[ConsoleWindow] ConcurrentModificationException")
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)
} }
@@ -148,7 +167,7 @@ class ConsoleWindow : UICanvas() {
val acceptedChars = "1234567890-=qwfpgjluy;[]\\arstdhneio'zxcvbkm,./!@#$%^&*()_+QWFPGJLUY:{}|ARSTDHNEIO\"ZXCVBKM<>? ".toSet() 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) { if (character in acceptedChars) {
commandInputPool!!.append(character) commandInputPool!!.append(character)
inputCursorPos += 1 inputCursorPos += 1
@@ -158,14 +177,14 @@ class ConsoleWindow : UICanvas() {
else { else {
return false return false
} }
} }*/
override fun keyUp(keycode: Int): Boolean { override fun keyUp(keycode: Int): Boolean {
return false return false
} }
private fun executeCommand() { private fun executeCommand(s: String) {
CommandInterpreter.execute(commandInputPool!!.toString()) CommandInterpreter.execute(s)
} }
fun sendMessage(msg: String) { fun sendMessage(msg: String) {
@@ -200,7 +219,7 @@ class ConsoleWindow : UICanvas() {
messagesCount = 0 messagesCount = 0
inputCursorPos = 0 inputCursorPos = 0
commandHistory = CircularArray<String>(COMMAND_HISTORY_MAX, true) commandHistory = CircularArray<String>(COMMAND_HISTORY_MAX, true)
commandInputPool = StringBuilder() textinput.clearText()
if (Authenticator.b()) { if (Authenticator.b()) {
sendMessage("$ccE${TerrarumAppConfiguration.GAME_NAME} ${App.getVERSION_STRING()} $EMDASH ${TerrarumAppConfiguration.COPYRIGHT_DATE_NAME}") 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(), drawOffY = MovementInterpolator.fastPullOut(openingTimeCounter.toFloat() / openCloseTime.toFloat(),
0f, -height.toFloat() 0f, -height.toFloat()
)*/ )*/
textinput.isActive = false
} }
override fun endOpening(delta: Float) { override fun endOpening(delta: Float) {
drawOffY = 0f drawOffY = 0f
openingTimeCounter = 0f openingTimeCounter = 0f
textinput.isActive = true
} }
override fun endClosing(delta: Float) { override fun endClosing(delta: Float) {
@@ -252,5 +273,6 @@ class ConsoleWindow : UICanvas() {
} }
override fun dispose() { override fun dispose() {
uiItems.forEach { it.dispose() }
} }
} }

View File

@@ -9,7 +9,6 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.FrameBuffer import com.badlogic.gdx.graphics.glutils.FrameBuffer
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.gamecontroller.* import net.torvald.terrarum.gamecontroller.*
import net.torvald.terrarum.utils.Clipboard import net.torvald.terrarum.utils.Clipboard
import net.torvald.terrarumsansbitmap.gdx.CodepointSequence 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 * 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. * 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 candidatesBackCol = TEXTINPUT_COL_BACKGROUND.cpy().mul(1f,1f,1f,1.5f)
private val candidateNumberStrWidth = App.fontGame.getWidth("8. ") private val candidateNumberStrWidth = App.fontGame.getWidth("8. ")
private fun getIME(): TerrarumInputMethod? { private fun getIME(): TerrarumIME? {
if (!imeOn) return null if (!imeOn) return null
val selectedIME = App.getConfigString("inputmethod") val selectedIME = App.getConfigString("inputmethod")
@@ -165,100 +165,105 @@ class UIItemTextLineInput(
val (eventType, char, headkey, repeatCount, keycodes) = e val (eventType, char, headkey, repeatCount, keycodes) = e
if (eventType == InputStrober.KEY_DOWN || eventType == InputStrober.KEY_CHANGE) { try {
fboUpdateLatch = true if (eventType == InputStrober.KEY_DOWN || eventType == InputStrober.KEY_CHANGE) {
forceLitCursor() fboUpdateLatch = true
val ime = getIME() forceLitCursor()
val ime = getIME()
if (keycodes.contains(App.getConfigInt("control_key_toggleime")) && repeatCount == 1) { if (keycodes.contains(App.getConfigInt("control_key_toggleime")) && repeatCount == 1) {
toggleIME() 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()) }
} }
else if (cursorX <= 0) { else if (keycodes.contains(Input.Keys.V) && (keycodes.contains(Input.Keys.CONTROL_LEFT) || keycodes.contains(Input.Keys.CONTROL_RIGHT))) {
cursorX = 0
cursorDrawX = 0
cursorDrawScroll = 0
}
else {
endComposing() endComposing()
if (cursorX > 0) { paste(Clipboard.fetch().substringBefore('\n').substringBefore('\t').toCodePoints())
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()
}
} }
} else if (keycodes.contains(Input.Keys.C) && (keycodes.contains(Input.Keys.CONTROL_LEFT) || keycodes.contains(Input.Keys.CONTROL_RIGHT))) {
else if (keycodes.contains(Input.Keys.LEFT)) { endComposing()
endComposing() copyToClipboard()
}
if (cursorX > 0) { else if (keycodes.contains(Input.Keys.BACKSPACE)) {
cursorX -= 1 if (ime != null && ime.composing()) {
cursorDrawX = App.fontGame.getWidth(CodepointSequence(textbuf.subList(0, cursorX))) candidates = ime.backspace().map { CodepointSequence(it.toCodePoints()) }
tryCursorForward() }
if (cursorX <= 0) { else if (cursorX <= 0) {
cursorX = 0 cursorX = 0
cursorDrawX = 0 cursorDrawX = 0
cursorDrawScroll = 0 cursorDrawScroll = 0
} }
} else {
} endComposing()
else if (keycodes.contains(Input.Keys.RIGHT)) { if (cursorX > 0) {
endComposing() 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) { cursorDrawX = App.fontGame.getWidth(CodepointSequence(textbuf.subList(0, cursorX)))
cursorX += 1 tryCursorForward()
cursorDrawX = App.fontGame.getWidth(CodepointSequence(textbuf.subList(0, cursorX))) }
tryCursorBack() }
} }
} else if (keycodes.contains(Input.Keys.LEFT)) {
// accept: endComposing()
// - 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) { if (cursorX > 0) {
val newStatus = ime.acceptChar(headkey, shiftin, altgrin, char) cursorX -= 1
candidates = newStatus.first.map { CodepointSequence(it.toCodePoints()) } cursorDrawX = App.fontGame.getWidth(CodepointSequence(textbuf.subList(0, cursorX)))
tryCursorForward()
newStatus.second.toCodePoints() 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()}") // println("textinput codepoints: ${codepoints.map { it.toString(16) }.joinToString()}")
if (!maxLen.exceeds(textbuf, codepoints)) { if (!maxLen.exceeds(textbuf, codepoints)) {
textbuf.addAll(cursorX, codepoints) textbuf.addAll(cursorX, codepoints)
cursorX += codepoints.size cursorX += codepoints.size
cursorDrawX = App.fontGame.getWidth(CodepointSequence(textbuf.subList(0, cursorX))) 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) { if (textbuf.size == 0) {
@@ -454,7 +459,7 @@ class UIItemTextLineInput(
// draw candidates view // draw candidates view
if (candidates.isNotEmpty()) { if (candidates.isNotEmpty()) {
val textWidths = candidates.map { App.fontGame.getWidth(CodepointSequence(it)) } 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 candidatesCount = minOf(candidatesMax, candidates.size)
val isOnecolumn = (candidatesCount <= 3) val isOnecolumn = (candidatesCount <= 3)
val halfcount = if (isOnecolumn) candidatesCount else FastMath.ceil(candidatesCount / 2f) val halfcount = if (isOnecolumn) candidatesCount else FastMath.ceil(candidatesCount / 2f)
@@ -510,6 +515,17 @@ class UIItemTextLineInput(
fun getText() = textbufToString() fun getText() = textbufToString()
fun getTextOrPlaceholder(): String = if (textbuf.isEmpty()) currentPlaceholderText.toJavaString() else getText() 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() { override fun dispose() {
fbo.dispose() fbo.dispose()