diff --git a/src/main/java/de/bixilon/minosoft/data/text/BaseComponent.kt b/src/main/java/de/bixilon/minosoft/data/text/BaseComponent.kt index 11049f8b0..693646764 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/BaseComponent.kt +++ b/src/main/java/de/bixilon/minosoft/data/text/BaseComponent.kt @@ -268,4 +268,25 @@ class BaseComponent : ChatComponent { } return parts == other.parts } + + override val length: Int + get() { + var length = 0 + for (part in parts) { + length += part.length + } + return length + } + + override fun getTextAt(pointer: Int): TextComponent { + var pointer = pointer + for (part in parts) { + val length = part.length + if (pointer < length) { + return part.getTextAt(pointer) + } + pointer -= length + } + throw IllegalArgumentException("Pointer ot of bounds!") + } } diff --git a/src/main/java/de/bixilon/minosoft/data/text/ChatComponent.kt b/src/main/java/de/bixilon/minosoft/data/text/ChatComponent.kt index 06b02f896..9f3e4f2c1 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/ChatComponent.kt +++ b/src/main/java/de/bixilon/minosoft/data/text/ChatComponent.kt @@ -67,6 +67,16 @@ interface ChatComponent { */ fun applyDefaultColor(color: RGBColor) + /** + * @return The current text component at a specific pointer (char offset) + */ + fun getTextAt(pointer: Int): TextComponent + + /** + * The length in chars + */ + val length: Int + companion object { val EMPTY = ChatComponent.of("") diff --git a/src/main/java/de/bixilon/minosoft/data/text/TextComponent.kt b/src/main/java/de/bixilon/minosoft/data/text/TextComponent.kt index 8263e408d..693a085df 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/TextComponent.kt +++ b/src/main/java/de/bixilon/minosoft/data/text/TextComponent.kt @@ -211,10 +211,20 @@ open class TextComponent( color = color, formatting = formatting, clickEvent = clickEvent, - hoverEvent = hoverEvent + hoverEvent = hoverEvent, ) } + override val length: Int + get() = message.length + + override fun getTextAt(pointer: Int): TextComponent { + if (pointer < 0 || pointer > message.length) { + throw IllegalArgumentException("Pointer out of bounds: $pointer") + } + return this + } + override fun hashCode(): Int { return message.hashCode() } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextElement.kt index 34b9383fb..94192f21b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextElement.kt @@ -18,6 +18,7 @@ import de.bixilon.kutil.primitive.BooleanUtil.decide import de.bixilon.minosoft.Minosoft import de.bixilon.minosoft.data.text.ChatComponent import de.bixilon.minosoft.data.text.RGBColor +import de.bixilon.minosoft.data.text.TextComponent import de.bixilon.minosoft.gui.rendering.RenderConstants import de.bixilon.minosoft.gui.rendering.font.Font import de.bixilon.minosoft.gui.rendering.font.renderer.ChatComponentRenderer @@ -27,6 +28,8 @@ 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.HorizontalAlignments.Companion.getOffset import de.bixilon.minosoft.gui.rendering.gui.elements.InfiniteSizeElement +import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions +import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMesh import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions @@ -168,6 +171,28 @@ open class TextElement( renderInfo.currentLineNumber = 0 } + override fun onMouseAction(position: Vec2i, button: MouseButtons, action: MouseActions) { + if (action != MouseActions.PRESS || button != MouseButtons.LEFT) { + return + } + val text = getTextComponentAt(position) ?: return + println(text) + } + + private fun getTextComponentAt(position: Vec2i): TextComponent? { + val offset = Vec2i(position) + val line = renderInfo.lines.getOrNull(offset.y / charHeight) ?: return null + offset.y = offset.y % charHeight + + val textElement = TextElement(guiRenderer, line.text, fontAlignment, false, backgroundColor, noBorder, parent, scale) + textElement._prefMaxSize = Vec2i(offset.x, charHeight) + textElement.forceSilentApply() + + offset.x += fontAlignment.getOffset(size.x, line.width) + + return line.text.getTextAt(textElement.renderInfo.lines.getOrNull(0)?.text?.message?.length ?: return null) + } + override fun toString(): String { return chatComponent.toString() } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextFlowElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextFlowElement.kt index 61b37f46d..c086513fe 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextFlowElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextFlowElement.kt @@ -22,6 +22,8 @@ import de.bixilon.minosoft.gui.rendering.font.Font 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.input.mouse.MouseActions +import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY @@ -178,6 +180,21 @@ open class TextFlowElement( } } + override fun onMouseAction(position: Vec2i, button: MouseButtons, action: MouseActions) { + val pair = getAt(position) ?: return + pair.first.textElement.onMouseAction(pair.second, button, action) + } + + private fun getAt(position: Vec2i): Pair? { + val reversedY = size.y - position.y + val line = visibleLines.getOrNull(reversedY / Font.TOTAL_CHAR_HEIGHT) ?: return null + if (position.x > line.textElement.size.x) { + return null + } + val offset = Vec2i(position.x, reversedY % Font.TOTAL_CHAR_HEIGHT) + return Pair(line, offset) + } + override fun tick() { checkExpiredLines() }