diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/mark/MarkTextElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/mark/MarkTextElement.kt
new file mode 100644
index 000000000..dd511e63e
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/mark/MarkTextElement.kt
@@ -0,0 +1,103 @@
+/*
+ * Minosoft
+ * Copyright (C) 2022 Moritz Zwerger
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ *
+ * This software is not affiliated with Mojang AB, the original developer of Minecraft.
+ */
+
+package de.bixilon.minosoft.gui.rendering.gui.elements.text.mark
+
+import de.bixilon.minosoft.config.key.KeyCodes
+import de.bixilon.minosoft.data.text.ChatComponent
+import de.bixilon.minosoft.data.text.RGBColor
+import de.bixilon.minosoft.gui.rendering.RenderConstants
+import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
+import de.bixilon.minosoft.gui.rendering.gui.elements.Element
+import de.bixilon.minosoft.gui.rendering.gui.elements.HorizontalAlignments
+import de.bixilon.minosoft.gui.rendering.gui.elements.text.TextElement
+import de.bixilon.minosoft.gui.rendering.gui.input.ModifierKeys
+import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
+import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
+import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes
+import glm_.vec2.Vec2i
+
+class MarkTextElement(
+ guiRenderer: GUIRenderer,
+ text: Any,
+ fontAlignment: HorizontalAlignments = HorizontalAlignments.LEFT,
+ background: Boolean = true,
+ backgroundColor: RGBColor = RenderConstants.TEXT_BACKGROUND_COLOR,
+ noBorder: Boolean = false,
+ parent: Element? = null,
+ scale: Float = 1.0f,
+) : TextElement(guiRenderer, text, fontAlignment, background, backgroundColor, noBorder, parent, scale) {
+ var markStartPosition = 0
+ var markEndPosition = 0
+
+ val marked: Boolean
+ get() = markStartPosition >= 0
+
+ val markedText: String
+ get() {
+ if (!marked) {
+ return ""
+ }
+ return chatComponent.message.substring(markStartPosition, markEndPosition)
+ }
+
+ override var chatComponent: ChatComponent
+ get() = super.chatComponent
+ set(value) {
+ super.chatComponent = value
+ unmark()
+ }
+
+ fun mark(start: Int, end: Int) {
+ markStartPosition = start
+ markEndPosition = end
+ forceSilentApply()
+ }
+
+ fun unmark() {
+ if (!marked) {
+ return
+ }
+ markStartPosition = -1
+ markEndPosition = -1
+ forceSilentApply()
+ }
+
+ override fun forceRender(offset: Vec2i, z: Int, consumer: GUIVertexConsumer, options: GUIVertexOptions?): Int {
+
+ if (markStartPosition >= 0) {
+ for (line in renderInfo.lines) {
+ // ToDo
+ }
+ }
+
+ return super.forceRender(offset, z, consumer, options) + 1
+ }
+
+ override fun onKey(key: KeyCodes, type: KeyChangeTypes) {
+ super.onKey(key, type)
+
+ val controlDown = guiRenderer.isKeyDown(ModifierKeys.CONTROL)
+
+ when (key) {
+ KeyCodes.KEY_A -> {
+ if (!controlDown) {
+ return
+ }
+ mark(0, chatComponent.message.length)
+ }
+ KeyCodes.KEY_ESCAPE -> unmark()
+ else -> return
+ }
+ }
+}
diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/mark/TextCursorStyles.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/mark/TextCursorStyles.kt
new file mode 100644
index 000000000..2818e676c
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/mark/TextCursorStyles.kt
@@ -0,0 +1,36 @@
+/*
+ * Minosoft
+ * Copyright (C) 2022 Moritz Zwerger
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ *
+ * This software is not affiliated with Mojang AB, the original developer of Minecraft.
+ */
+
+package de.bixilon.minosoft.gui.rendering.gui.elements.text.mark
+
+enum class TextCursorStyles {
+ /**
+ * The cursor is always hidden
+ */
+ DISABLED,
+
+ /**
+ * Cursor is only visible when text is marked
+ */
+ MARKED,
+
+ /**
+ * Cursor is visible when something is marked, or you clicked in
+ */
+ CLICKED,
+
+ /**
+ * Cursor is always visible
+ */
+ ALWAYS,
+}
diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/elements/input/TextInputElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/elements/input/TextInputElement.kt
index f648f4c9d..1de523ec6 100644
--- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/elements/input/TextInputElement.kt
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/elements/input/TextInputElement.kt
@@ -20,6 +20,8 @@ import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.elements.Element
import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.ColorElement
import de.bixilon.minosoft.gui.rendering.gui.elements.text.TextElement
+import de.bixilon.minosoft.gui.rendering.gui.elements.text.mark.MarkTextElement
+import de.bixilon.minosoft.gui.rendering.gui.elements.text.mark.TextCursorStyles
import de.bixilon.minosoft.gui.rendering.gui.input.ModifierKeys
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
@@ -30,33 +32,36 @@ import glm_.vec2.Vec2i
class TextInputElement(
guiRenderer: GUIRenderer,
val maxLength: Int = Int.MAX_VALUE,
+ val cursorStyles: TextCursorStyles = TextCursorStyles.CLICKED,
) : Element(guiRenderer) {
private val cursor = ColorElement(guiRenderer, size = Vec2i(1, Font.TOTAL_CHAR_HEIGHT))
- private val textElement = TextElement(guiRenderer, "", background = false, parent = this)
+ private val textElement = MarkTextElement(guiRenderer, "", background = false, parent = this)
private val background = ColorElement(guiRenderer, Vec2i.EMPTY, RenderConstants.TEXT_BACKGROUND_COLOR)
private var cursorOffset: Vec2i = Vec2i.EMPTY
- private var _value: String = ""
+ private val _value = StringBuffer(256)
var value: String
- get() = _value
+ get() = _value.toString()
set(value) {
pointer = 0
- if (_value == value) {
+ if (_value.equals(value)) {
return
}
- _value = value
+ _value.replace(0, _value.length, value)
forceApply()
}
private var pointer = 0
private var cursorTick = 0
+
override fun forceRender(offset: Vec2i, z: Int, consumer: GUIVertexConsumer, options: GUIVertexOptions?): Int {
- background.render(offset, z, consumer, options)
- textElement.render(offset, z + 1, consumer, options)
+ var zOffset = background.render(offset, z, consumer, options)
+
+ zOffset += textElement.render(offset, z + zOffset, consumer, options)
if (cursorTick < 20) {
- cursor.render(offset + cursorOffset, z + 1 + TextElement.LAYERS, consumer, options)
+ cursor.render(offset + cursorOffset, z + zOffset, consumer, options)
}
- return TextElement.LAYERS + 2
+ return zOffset + 1
}
override fun forceSilentApply() {
@@ -84,9 +89,13 @@ class TextInputElement(
cacheUpToDate = false
}
- private fun silentAppend(string: String) {
- val appendLength = minOf(string.length, maxLength - _value.length)
- _value = _value.substring(0, pointer) + string.substring(0, appendLength) + _value.substring(pointer, _value.length)
+ private fun insert(string: String) {
+ val insert = string.replace("\n", "").replace("\r", "").replace('ยง', '&')
+ if (textElement.markStartPosition > 0) {
+ _value.delete(textElement.markStartPosition, textElement.markEndPosition)
+ }
+ val appendLength = minOf(insert.length, maxLength - _value.length)
+ _value.insert(pointer, insert.substring(0, appendLength))
pointer += appendLength
}
@@ -95,48 +104,83 @@ class TextInputElement(
return
}
cursorTick = CURSOR_TICK_ON_ACTION
- silentAppend(char.toChar().toString())
+ insert(char.toChar().toString())
forceApply()
}
+ private fun mark(mark: Boolean, right: Boolean) {
+ if (mark) {
+ var start: Int = textElement.markStartPosition
+ var end: Int = textElement.markEndPosition
+ if (right) {
+ if (start < 0) {
+ start = pointer
+ end = start
+ }
+ end++
+ } else {
+ if (start < 0) {
+ end = pointer
+ start = end
+ }
+ start--
+ }
+ textElement.mark(start, end)
+ } else {
+ textElement.unmark()
+ }
+ }
+
override fun onKey(key: KeyCodes, type: KeyChangeTypes) {
if (type == KeyChangeTypes.RELEASE) {
return
}
val controlDown = guiRenderer.isKeyDown(ModifierKeys.CONTROL)
+ val shiftDown = guiRenderer.isKeyDown(ModifierKeys.SHIFT)
cursorTick = CURSOR_TICK_ON_ACTION
when (key) {
KeyCodes.KEY_V -> {
if (controlDown) {
- silentAppend(guiRenderer.renderWindow.window.clipboardText)
+ insert(guiRenderer.renderWindow.window.clipboardText)
+ }
+ }
+ KeyCodes.KEY_C -> {
+ if (controlDown) {
+ val markedText = textElement.markedText
+ if (markedText.isEmpty()) {
+ return
+ }
+ renderWindow.window.clipboardText = markedText
}
}
KeyCodes.KEY_BACKSPACE -> {
if (pointer == 0 || _value.isEmpty()) {
return
}
- _value = _value.removeRange(pointer - 1, pointer)
+ _value.deleteCharAt(pointer - 1)
pointer--
}
KeyCodes.KEY_DELETE -> {
if (pointer == _value.length || _value.isEmpty()) {
return
}
- _value = _value.removeRange(pointer, pointer + 1)
+ _value.deleteCharAt(pointer)
}
KeyCodes.KEY_LEFT -> {
if (pointer == 0) {
return
}
+ mark(shiftDown, false)
pointer--
}
KeyCodes.KEY_RIGHT -> {
if (pointer == _value.length) {
return
}
+ mark(shiftDown, true)
pointer++
}
- else -> return
+ else -> return textElement.onKey(key, type)
}
forceApply()
}