diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/gui/GuiRenderTestUtil.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/gui/GuiRenderTestUtil.kt
new file mode 100644
index 000000000..76e9f145f
--- /dev/null
+++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/gui/GuiRenderTestUtil.kt
@@ -0,0 +1,59 @@
+/*
+ * Minosoft
+ * Copyright (C) 2020-2023 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
+
+import de.bixilon.kotlinglm.vec2.Vec2
+import de.bixilon.kotlinglm.vec2.Vec2i
+import de.bixilon.kutil.observer.DataObserver
+import de.bixilon.kutil.reflection.ReflectionUtil.forceSet
+import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
+import de.bixilon.minosoft.gui.rendering.RenderContext
+import de.bixilon.minosoft.gui.rendering.font.manager.FontManager
+import de.bixilon.minosoft.gui.rendering.font.types.dummy.DummyFontType
+import de.bixilon.minosoft.gui.rendering.gui.atlas.CodeTexturePart
+import de.bixilon.minosoft.gui.rendering.gui.elements.Element
+import de.bixilon.minosoft.gui.rendering.system.dummy.DummyRenderSystem
+import de.bixilon.minosoft.gui.rendering.system.dummy.texture.DummyTexture
+import de.bixilon.minosoft.gui.rendering.system.dummy.texture.DummyTextureManager
+import de.bixilon.minosoft.test.IT.OBJENESIS
+import org.testng.Assert.assertEquals
+
+object GuiRenderTestUtil {
+
+ private fun createContext(): RenderContext {
+ val context = OBJENESIS.newInstance(RenderContext::class.java)
+ context.font = FontManager(DummyFontType)
+ context::renderSystem.forceSet(DummyRenderSystem(context))
+ context::textureManager.forceSet(DummyTextureManager(context))
+
+ context.textureManager::whiteTexture.forceSet(CodeTexturePart(DummyTexture(minosoft("white")), size = Vec2i(16, 16)))
+
+ return context
+ }
+
+ fun create(size: Vec2 = Vec2(1920.0f, 1080.0f)): GUIRenderer {
+ val renderer = OBJENESIS.newInstance(GUIRenderer::class.java)
+ renderer::scaledSize.forceSet(DataObserver(size))
+ renderer::halfSize.forceSet(size / 2.0f)
+
+ renderer::context.forceSet(createContext())
+
+ return renderer
+ }
+
+
+ fun Element.assetSize(size: Vec2) {
+ assertEquals(size, this.size)
+ }
+}
diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextElementTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextElementTest.kt
new file mode 100644
index 000000000..bbe4d96ba
--- /dev/null
+++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextElementTest.kt
@@ -0,0 +1,63 @@
+package de.bixilon.minosoft.gui.rendering.gui.elements.text
+
+import de.bixilon.kotlinglm.vec2.Vec2
+import de.bixilon.kotlinglm.vec4.Vec4
+import de.bixilon.minosoft.gui.rendering.gui.GuiRenderTestUtil
+import de.bixilon.minosoft.gui.rendering.gui.GuiRenderTestUtil.assetSize
+import de.bixilon.minosoft.gui.rendering.gui.elements.text.background.TextBackground
+import org.testng.annotations.Test
+
+@Test(groups = ["font", "gui"])
+class TextElementTest {
+
+ fun `size empty`() {
+ val element = TextElement(GuiRenderTestUtil.create(), "")
+ element.assetSize(Vec2(0, 0))
+ }
+
+ fun `size of single char`() {
+ val element = TextElement(GuiRenderTestUtil.create(), "b", background = null)
+ element.assetSize(Vec2(0.5f, 11.0f))
+ }
+
+ fun `size of multiple chars`() {
+ val element = TextElement(GuiRenderTestUtil.create(), "bc", background = null)
+ element.assetSize(Vec2(2.5f, 11.0f))
+ }
+
+ fun `size with new line`() {
+ val element = TextElement(GuiRenderTestUtil.create(), "bc\nbc", background = null)
+ element.assetSize(Vec2(2.5f, 22.0f))
+ }
+
+ fun `size with background`() {
+ val element = TextElement(GuiRenderTestUtil.create(), "bc")
+ element.assetSize(Vec2(4.5f, 13.0f))
+ }
+
+ fun `size with background and newlines`() {
+ val element = TextElement(GuiRenderTestUtil.create(), "bc\nbc")
+ element.assetSize(Vec2(4.5f, 24.0f))
+ }
+
+ fun `size if text changed`() {
+ val element = TextElement(GuiRenderTestUtil.create(), "bc\nbc")
+ element.text = "bcd\nbcd\nbcd"
+ element.assetSize(Vec2(6.0f, 35.0f))
+ }
+
+ fun `size if background cleared`() {
+ val element = TextElement(GuiRenderTestUtil.create(), "bcd\nbcd\nbcd")
+ element.background = null
+ element.assetSize(Vec2(4.0f, 33.0f))
+ }
+
+ fun `size if background set`() {
+ val element = TextElement(GuiRenderTestUtil.create(), "bcd\nbcd\nbcd")
+ element.background = TextBackground(size = Vec4(2.0f))
+ element.assetSize(Vec2(8.0f, 37.0f))
+ }
+
+
+ // TODO: test on mouse (click/hover events), rendering, size limiting
+}
diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/atlas/CodeTexturePart.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/atlas/CodeTexturePart.kt
index 838e2836a..c1c3a4fae 100644
--- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/atlas/CodeTexturePart.kt
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/atlas/CodeTexturePart.kt
@@ -19,7 +19,7 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTex
class CodeTexturePart(
override val texture: AbstractTexture,
- override val uvStart: Vec2,
- override val uvEnd: Vec2,
+ override val uvStart: Vec2 = Vec2(0.0f),
+ override val uvEnd: Vec2 = Vec2(1.0f),
override val size: Vec2i,
) : TexturePart
diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/FadingTextElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/FadingTextElement.kt
index d937f0275..b5dd107c3 100644
--- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/FadingTextElement.kt
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/FadingTextElement.kt
@@ -15,14 +15,12 @@ package de.bixilon.minosoft.gui.rendering.gui.elements.text
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kutil.primitive.BooleanUtil.decide
-import de.bixilon.kutil.time.TimeUtil
import de.bixilon.kutil.time.TimeUtil.millis
-import de.bixilon.minosoft.data.text.formatting.color.RGBColor
-import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.font.renderer.element.TextRenderProperties
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.Pollable
+import de.bixilon.minosoft.gui.rendering.gui.elements.text.background.TextBackground
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions.Companion.copy
@@ -34,7 +32,7 @@ class FadingTextElement(
var fadeInTime: Long = 100,
var stayTime: Long = 1000,
var fadeOutTime: Long = 100,
- background: RGBColor? = RenderConstants.TEXT_BACKGROUND_COLOR,
+ background: TextBackground? = TextBackground.DEFAULT,
parent: Element? = null,
properties: TextRenderProperties,
) : TextElement(guiRenderer = guiRenderer, text = text, background = background, parent, properties), Pollable {
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 2a360a2b9..f3dcc894b 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
@@ -21,8 +21,6 @@ import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.data.text.EmptyComponent
import de.bixilon.minosoft.data.text.TextComponent
-import de.bixilon.minosoft.data.text.formatting.color.RGBColor
-import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.font.renderer.component.ChatComponentRenderer
import de.bixilon.minosoft.gui.rendering.font.renderer.element.LineRenderInfo
import de.bixilon.minosoft.gui.rendering.font.renderer.element.TextOffset
@@ -31,6 +29,7 @@ import de.bixilon.minosoft.gui.rendering.font.renderer.element.TextRenderPropert
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.Companion.getOffset
+import de.bixilon.minosoft.gui.rendering.gui.elements.text.background.TextBackground
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
@@ -39,13 +38,20 @@ import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.system.window.CursorShapes
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2Util.EMPTY
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2Util.MAX
+import de.bixilon.minosoft.gui.rendering.util.vec.vec4.Vec4Util.bottom
+import de.bixilon.minosoft.gui.rendering.util.vec.vec4.Vec4Util.offset
+import de.bixilon.minosoft.gui.rendering.util.vec.vec4.Vec4Util.vertical
import de.bixilon.minosoft.gui.rendering.util.vec.vec4.Vec4iUtil.offset
import de.bixilon.minosoft.util.KUtil.charCount
+/**
+ * A simple UI element that draws text on the screen
+ * A background color is supported, if set
+ */
open class TextElement(
guiRenderer: GUIRenderer,
text: Any,
- background: RGBColor? = RenderConstants.TEXT_BACKGROUND_COLOR,
+ background: TextBackground? = TextBackground.DEFAULT,
parent: Element? = null,
properties: TextRenderProperties = TextRenderProperties.DEFAULT,
) : Element(guiRenderer, text.charCount * 6 * GUIMesh.GUIMeshStruct.FLOATS_PER_VERTEX), Labeled {
@@ -53,13 +59,13 @@ open class TextElement(
lateinit var info: TextRenderInfo
private set
- var background: RGBColor? = background
+ var background: TextBackground? = background
set(value) {
if (field == value) {
return
}
field = value
- cacheUpToDate = false
+ forceApply()
}
var properties: TextRenderProperties = properties
set(value) {
@@ -132,7 +138,7 @@ open class TextElement(
override fun onChildChange(child: Element) = Broken("A TextElement can not have a child!")
- private fun GUIVertexConsumer.renderBackground(color: RGBColor, properties: TextRenderProperties, info: TextRenderInfo, offset: Vec2, options: GUIVertexOptions?) {
+ private fun GUIVertexConsumer.renderBackground(background: TextBackground, properties: TextRenderProperties, info: TextRenderInfo, offset: Vec2, options: GUIVertexOptions?) {
val start = Vec2()
val end = Vec2()
@@ -142,10 +148,15 @@ open class TextElement(
start.x = offset.x + properties.alignment.getOffset(line.width, info.size.x)
start.y = offset.y + (index * lineHeight) + (maxOf(index - 1, 0) * properties.lineSpacing)
- end.x = start.x + line.width
+ end.x = start.x + line.width + background.size.vertical
end.y = start.y + lineHeight
- addQuad(start, end, context.textureManager.whiteTexture, color, options)
+ if (index == info.lines.size - 1) {
+ // last line
+ end.y += background.size.bottom
+ }
+
+ addQuad(start, end, context.textureManager.whiteTexture, background.color, options)
}
}
@@ -154,8 +165,12 @@ open class TextElement(
val info = this.info
val properties = this.properties
val initialOffset = Vec2(offset + margin.offset)
+ val textOffset = Vec2(initialOffset)
- this.background?.let { consumer.renderBackground(it, properties, info, initialOffset, options) }
+ this.background?.let {
+ consumer.renderBackground(it, properties, info, initialOffset, options)
+ textOffset += it.size.offset
+ }
var vertices = ChatComponentRenderer.calculatePrimitiveCount(chatComponent) * consumer.order.size * GUIMesh.GUIMeshStruct.FLOATS_PER_VERTEX
if (properties.shadow) {
@@ -163,7 +178,7 @@ open class TextElement(
}
consumer.ensureSize(vertices)
- ChatComponentRenderer.render(TextOffset(Vec2(initialOffset)), context.font, properties, info, consumer, options, chatComponent)
+ ChatComponentRenderer.render(TextOffset(textOffset), context.font, properties, info, consumer, options, chatComponent)
info.rewind()
}
@@ -220,7 +235,7 @@ open class TextElement(
private fun TextRenderInfo.getLineAt(lineHeight: Float, lineSpacing: Float, offset: Float): Pair? {
var offset = offset
- for ((index, line) in info.lines.withIndex()) {
+ for ((index, line) in lines.withIndex()) {
if (offset in 0.0f..lineHeight) {
return Pair(line, offset)
}
diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/background/TextBackground.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/background/TextBackground.kt
new file mode 100644
index 000000000..cbebf7f1d
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/background/TextBackground.kt
@@ -0,0 +1,28 @@
+/*
+ * Minosoft
+ * Copyright (C) 2020-2023 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.background
+
+import de.bixilon.kotlinglm.vec4.Vec4
+import de.bixilon.minosoft.data.text.formatting.color.RGBColor
+import de.bixilon.minosoft.gui.rendering.RenderConstants
+
+data class TextBackground(
+ val color: RGBColor = RenderConstants.TEXT_BACKGROUND_COLOR,
+ val size: Vec4 = Vec4(1.0f),
+) {
+
+ companion object {
+ val DEFAULT = TextBackground()
+ }
+}
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
index 3371f50cc..ba137fb29 100644
--- 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
@@ -17,13 +17,12 @@ import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.data.text.formatting.color.ChatColors
-import de.bixilon.minosoft.data.text.formatting.color.RGBColor
-import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.font.renderer.element.TextRenderProperties
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.background.TextBackground
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
@@ -32,7 +31,7 @@ import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes
class MarkTextElement(
guiRenderer: GUIRenderer,
text: Any,
- background: RGBColor? = RenderConstants.TEXT_BACKGROUND_COLOR,
+ background: TextBackground? = TextBackground.DEFAULT,
parent: Element? = null,
properties: TextRenderProperties = TextRenderProperties.DEFAULT,
) : TextElement(guiRenderer, text, background, parent, properties) {
diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec4/Vec4Util.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec4/Vec4Util.kt
index 3a6d4bc36..3ba1896ba 100644
--- a/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec4/Vec4Util.kt
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec4/Vec4Util.kt
@@ -13,6 +13,7 @@
package de.bixilon.minosoft.gui.rendering.util.vec.vec4
+import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec4.Vec4
object Vec4Util {
@@ -26,5 +27,30 @@ object Vec4Util {
val Vec4.Companion.MAX: Vec4
get() = Vec4(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE)
+
+ val Vec4.top: Float
+ get() = this.x
+
+ val Vec4.right: Float
+ get() = this.y
+
+ val Vec4.bottom: Float
+ get() = this.z
+
+ val Vec4.left: Float
+ get() = this.w
+
+ val Vec4.horizontal: Float
+ get() = right + left
+
+ val Vec4.vertical: Float
+ get() = top + bottom
+
+ val Vec4.spaceSize: Vec2
+ get() = Vec2(horizontal, vertical)
+
+ val Vec4.offset: Vec2
+ get() = Vec2(left, top)
+
fun FloatArray.dot(x: Float, y: Float, z: Float) = this[0] * x + this[1] * y + this[2] * z + this[3]
}