rendering: Improve TextField

This commit is contained in:
Bixilon 2021-04-26 20:50:29 +02:00
parent 0412be8d63
commit 6a74e2b6dd
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
9 changed files with 63 additions and 49 deletions

View File

@ -44,7 +44,7 @@ object KeyBindingsNames {
val OPEN_CHAT = ResourceLocation("minosoft:open_chat") val OPEN_CHAT = ResourceLocation("minosoft:open_chat")
val CLOSE_CHAT = ResourceLocation("minosoft:close_chat") val CLOSE = ResourceLocation("minosoft:close")
val SELECT_HOTBAR_SLOTS = arrayOf( val SELECT_HOTBAR_SLOTS = arrayOf(
@ -194,10 +194,11 @@ object KeyBindingsNames {
KeyAction.PRESS to mutableSetOf(KeyCodes.KEY_T) KeyAction.PRESS to mutableSetOf(KeyCodes.KEY_T)
), ),
), ),
CLOSE_CHAT to KeyBinding( CLOSE to KeyBinding(
mutableMapOf( mutableMapOf(
KeyAction.PRESS to mutableSetOf(KeyCodes.KEY_ESCAPE) KeyAction.PRESS to mutableSetOf(KeyCodes.KEY_ESCAPE)
), ),
ignoreConsumer = true
), ),
) )
} }

View File

@ -257,4 +257,13 @@ class RenderWindowInputHandler(
fun unregisterKeyBinding(it: ResourceLocation) { fun unregisterKeyBinding(it: ResourceLocation) {
keyBindingCallbacks.remove(it) keyBindingCallbacks.remove(it)
} }
fun isKeyDown(vararg keys: KeyCodes): Boolean {
for (key in keys) {
if (keysDown.contains(key)) {
return true
}
}
return false
}
} }

View File

@ -72,6 +72,10 @@ class HUDRenderer(val connection: PlayConnection, val renderWindow: RenderWindow
for ((_, element) in hudElements.values) { for ((_, element) in hudElements.values) {
element.init() element.init()
} }
renderWindow.inputHandler.registerKeyCallback(KeyBindingsNames.CLOSE) {
}
} }
private fun registerDefaultElements() { private fun registerDefaultElements() {

View File

@ -17,7 +17,7 @@ import de.bixilon.minosoft.config.key.KeyCodes
interface KeyConsumer { interface KeyConsumer {
val focused: Boolean fun close()
fun charInput(char: Char) {} fun charInput(char: Char) {}

View File

@ -16,7 +16,6 @@ package de.bixilon.minosoft.gui.rendering.hud.elements.input
import glm_.vec2.Vec2i import glm_.vec2.Vec2i
interface MouseConsumer { interface MouseConsumer {
val focused: Boolean
fun mouseMove(position: Vec2i) {} fun mouseMove(position: Vec2i) {}
} }

View File

@ -24,11 +24,9 @@ import glm_.vec2.Vec2i
open class TextField( open class TextField(
renderWindow: RenderWindow, renderWindow: RenderWindow,
defaultText: String = "", val properties: TextFieldProperties,
var maxLength: Int = 256,
) : AbsoluteLayout(renderWindow), KeyConsumer, MouseConsumer { ) : AbsoluteLayout(renderWindow), KeyConsumer, MouseConsumer {
override var focused: Boolean = true private var textBuilder: StringBuilder = StringBuilder(properties.defaultText)
private var textBuilder: StringBuilder = StringBuilder(defaultText)
val textElement = LabelNode(renderWindow, sizing = sizing, text = ChatComponent.valueOf(raw = text), background = false) val textElement = LabelNode(renderWindow, sizing = sizing, text = ChatComponent.valueOf(raw = text), background = false)
private var position = text.length private var position = text.length
@ -57,7 +55,7 @@ open class TextField(
override fun keyInput(keyCodes: KeyCodes) { override fun keyInput(keyCodes: KeyCodes) {
when (keyCodes) { when (keyCodes) {
KeyCodes.KEY_BACKSPACE -> { KeyCodes.KEY_BACKSPACE -> {
if (textBuilder.isEmpty()) { if (textBuilder.isEmpty() || position == 0) {
return return
} }
textBuilder.deleteCharAt(--position) textBuilder.deleteCharAt(--position)
@ -69,10 +67,19 @@ open class TextField(
textBuilder.deleteCharAt(position) textBuilder.deleteCharAt(position)
} }
KeyCodes.KEY_ENTER -> { KeyCodes.KEY_ENTER -> {
if (position > maxLength) { if (renderWindow.inputHandler.isKeyDown(KeyCodes.KEY_LEFT_CONTROL, KeyCodes.KEY_RIGHT_CONTROL, KeyCodes.KEY_LEFT_SHIFT, KeyCodes.KEY_RIGHT_SHIFT)) {
// new line
if (position > properties.maxLength) {
return return
} }
textBuilder.insert(position++, '\n') textBuilder.insert(position++, '\n')
return
}
properties.onSubmit(text)
if (properties.submitCloses) {
properties.onClose()
}
text = properties.defaultText
} }
KeyCodes.KEY_LEFT -> { KeyCodes.KEY_LEFT -> {
position = MMath.clamp(position - 1, 0, text.length) position = MMath.clamp(position - 1, 0, text.length)
@ -92,18 +99,28 @@ open class TextField(
} }
override fun tick(tick: Long) { override fun tick(tick: Long) {
if ((tick / 8) % 2L == 0L && position == text.length) { if ((tick / FIELD_CURSOR_BLINK_INTERVAL) % 2L == 0L && position == text.length) {
textElement.sText = "$textBuilder" + "_" textElement.sText = "$textBuilder" + "_"
} else { } else {
textElement.sText = textBuilder.toString() textElement.sText = textBuilder.toString()
} }
} }
override fun close() {
properties.onClose()
}
override fun charInput(char: Char) { override fun charInput(char: Char) {
if (position >= maxLength) { if (position >= properties.maxLength) {
return return
} }
val previous = textBuilder.toString()
textBuilder.insert(position++, char.toString()) textBuilder.insert(position++, char.toString())
properties.onInput(previous, textBuilder.toString())
update() update()
} }
companion object {
const val FIELD_CURSOR_BLINK_INTERVAL = 8
}
} }

View File

@ -13,28 +13,11 @@
package de.bixilon.minosoft.gui.rendering.hud.elements.input package de.bixilon.minosoft.gui.rendering.hud.elements.input
import de.bixilon.minosoft.config.key.KeyCodes data class TextFieldProperties(
import de.bixilon.minosoft.gui.rendering.RenderWindow var defaultText: String = "",
var maxLength: Int = 256,
class SubmittableTextField( var onSubmit: (text: String) -> Unit = {},
renderWindow: RenderWindow, var onClose: () -> Unit = {},
defaultText: String = "", var onInput: (previousText: String, nextText: String) -> Unit = { _, _ -> },
maxLength: Int = 256, var submitCloses: Boolean = false,
private val onSubmit: (text: String) -> Unit, )
) : TextField(renderWindow, defaultText, maxLength) {
fun submit() {
onSubmit.invoke(text)
text = ""
}
override fun keyInput(keyCodes: KeyCodes) {
if (keyCodes == KeyCodes.KEY_ENTER) {
// submit
submit()
return
}
super.keyInput(keyCodes)
}
}

View File

@ -18,7 +18,8 @@ import de.bixilon.minosoft.config.config.game.controls.KeyBindingsNames
import de.bixilon.minosoft.gui.rendering.RenderConstants import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.font.Font import de.bixilon.minosoft.gui.rendering.font.Font
import de.bixilon.minosoft.gui.rendering.hud.HUDRenderer import de.bixilon.minosoft.gui.rendering.hud.HUDRenderer
import de.bixilon.minosoft.gui.rendering.hud.elements.input.SubmittableTextField import de.bixilon.minosoft.gui.rendering.hud.elements.input.TextField
import de.bixilon.minosoft.gui.rendering.hud.elements.input.TextFieldProperties
import de.bixilon.minosoft.gui.rendering.hud.nodes.HUDElement import de.bixilon.minosoft.gui.rendering.hud.nodes.HUDElement
import de.bixilon.minosoft.gui.rendering.hud.nodes.primitive.ImageNode import de.bixilon.minosoft.gui.rendering.hud.nodes.primitive.ImageNode
import de.bixilon.minosoft.gui.rendering.hud.nodes.properties.NodeSizing import de.bixilon.minosoft.gui.rendering.hud.nodes.properties.NodeSizing
@ -28,14 +29,18 @@ import de.bixilon.minosoft.util.MMath
import glm_.vec2.Vec2i import glm_.vec2.Vec2i
class ChatBoxHUDElement(hudRenderer: HUDRenderer) : HUDElement(hudRenderer), ScreenResizeCallback { class ChatBoxHUDElement(hudRenderer: HUDRenderer) : HUDElement(hudRenderer), ScreenResizeCallback {
private lateinit var inputField: SubmittableTextField private lateinit var inputField: TextField
private var inputFieldBackground = ImageNode(hudRenderer.renderWindow, sizing = NodeSizing(margin = Spacing(left = 1, right = 1)), textureLike = hudRenderer.renderWindow.WHITE_TEXTURE, z = 0, tintColor = RenderConstants.TEXT_BACKGROUND_COLOR) private var inputFieldBackground = ImageNode(hudRenderer.renderWindow, sizing = NodeSizing(margin = Spacing(left = 1, right = 1)), textureLike = hudRenderer.renderWindow.WHITE_TEXTURE, z = 0, tintColor = RenderConstants.TEXT_BACKGROUND_COLOR)
override fun init() { override fun init() {
inputField = SubmittableTextField(renderWindow = hudRenderer.renderWindow, maxLength = 256, onSubmit = { inputField = TextField(
hudRenderer.renderWindow.connection.sender.sendChatMessage(it) renderWindow = hudRenderer.renderWindow,
closeChat() properties = TextFieldProperties(
}) maxLength = 256,
submitCloses = true,
onSubmit = { hudRenderer.renderWindow.connection.sender.sendChatMessage(it) },
onClose = { closeChat() },
))
layout.addChild(Vec2i(0, 0), inputField) layout.addChild(Vec2i(0, 0), inputField)
inputField.apply() inputField.apply()
@ -43,9 +48,6 @@ class ChatBoxHUDElement(hudRenderer: HUDRenderer) : HUDElement(hudRenderer), Scr
hudRenderer.renderWindow.inputHandler.registerKeyCallback(KeyBindingsNames.OPEN_CHAT) { hudRenderer.renderWindow.inputHandler.registerKeyCallback(KeyBindingsNames.OPEN_CHAT) {
openChat() openChat()
} }
hudRenderer.renderWindow.inputHandler.registerKeyCallback(KeyBindingsNames.CLOSE_CHAT) {
closeChat()
}
} }
override fun onScreenResize(screenDimensions: Vec2i) { override fun onScreenResize(screenDimensions: Vec2i) {
@ -63,7 +65,6 @@ class ChatBoxHUDElement(hudRenderer: HUDRenderer) : HUDElement(hudRenderer), Scr
fun closeChat() { fun closeChat() {
layout.removeChild(inputFieldBackground) layout.removeChild(inputFieldBackground)
inputField.clearText()
hudRenderer.renderWindow.inputHandler.currentKeyConsumer = null hudRenderer.renderWindow.inputHandler.currentKeyConsumer = null
} }
} }

View File

@ -59,7 +59,7 @@ public class PacketSender {
if (this.connection.fireEvent(event)) { if (this.connection.fireEvent(event)) {
return; return;
} }
Log.log(LogMessageType.CHAT_OUT, "Sending chat message: %s", message); Log.log(LogMessageType.CHAT_OUT, message);
this.connection.sendPacket(new ChatMessageC2SP(event.getMessage())); this.connection.sendPacket(new ChatMessageC2SP(event.getMessage()));
} }