ASMR/src/main/kotlin/net/shadowfacts/cacao/view/Label.kt

111 lines
3.0 KiB
Kotlin

package net.shadowfacts.cacao.view
import net.minecraft.client.MinecraftClient
import net.minecraft.client.font.TextRenderer
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Size
import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.util.RenderHelper
/**
* A simple View that displays text. Allows for controlling the color and shadow of the text. Label cannot be used
* for multi-line text, instead use [TextView].
*
* @author shadowfacts
* @param text The text of this label.
* @param shadow Whether the text should be rendered with a shadow or not.
* @param maxLines The maximum number of lines of text to render.
* `0` means that there is no line limit and all lines will be rendered.
* When using a non-zero [maxLines] value, the [intrinsicContentSize] of the Label still assumes all
* content on one line. So, constraints must be provided to calculate the actual width to use for line
* wrapping.
*/
class Label(
text: String,
val shadow: Boolean = true,
val maxLines: Int = 0,
val wrappingMode: WrappingMode = WrappingMode.WRAP
): View() {
companion object {
private val textRenderer: TextRenderer
get() = MinecraftClient.getInstance().textRenderer
}
enum class WrappingMode {
WRAP, NO_WRAP
}
/**
* The text of this label. Mutating this field will update the intrinsic content size and trigger a layout.
*/
var text: String = text
set(value) {
field = value
updateIntrinsicContentSize()
// todo: setNeedsLayout instead of force unwrapping window
window!!.layout()
}
private lateinit var lines: List<String>
var textColor = Color.WHITE
set(value) {
field = value
textColorARGB = value.argb
}
private var textColorARGB: Int = textColor.argb
private val drawFunc by lazy {
if (shadow) textRenderer::drawWithShadow else textRenderer::draw
}
override fun wasAdded() {
super.wasAdded()
updateIntrinsicContentSize()
}
private fun updateIntrinsicContentSize() {
if (RenderHelper.disabled) return
val width = textRenderer.getStringWidth(text)
val height = textRenderer.fontHeight
intrinsicContentSize = Size(width.toDouble(), height.toDouble())
}
override fun drawContent(mouse: Point, delta: Float) {
if (!this::lines.isInitialized) {
computeLines()
}
for (i in 0 until lines.size) {
val y = i * textRenderer.fontHeight
drawFunc(lines[i], 0f, y.toFloat(), textColorARGB)
}
}
override fun didLayout() {
super.didLayout()
computeLines()
}
private fun computeLines() {
var lines = text.split("\n")
if (wrappingMode == WrappingMode.WRAP) {
lines = lines.flatMap {
wrapStringToWidthAsList(it, bounds.width)
}
}
if (0 < maxLines && maxLines < lines.size) {
lines = lines.dropLast(lines.size - maxLines)
}
this.lines = lines
}
private fun wrapStringToWidthAsList(string: String, width: Double): List<String> {
if (RenderHelper.disabled) return listOf(string)
return textRenderer.wrapStringToWidthAsList(string, width.toInt())
}
}