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 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 { if (RenderHelper.disabled) return listOf(string) return textRenderer.wrapStringToWidthAsList(string, width.toInt()) } }