From c79b93a4f9a0c229fa822994c94b3a53ce54a47f Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sun, 23 Jun 2019 19:58:06 -0400 Subject: [PATCH] Add NinePatchTexture and NinePatchView --- .../net/shadowfacts/asmr/TestCacaoScreen.kt | 18 ++- .../cacao/util/NinePatchTexture.kt | 51 +++++++++ .../shadowfacts/cacao/util/RenderHelper.kt | 15 ++- .../shadowfacts/cacao/view/NinePatchView.kt | 107 ++++++++++++++++++ 4 files changed, 176 insertions(+), 15 deletions(-) create mode 100644 src/main/kotlin/net/shadowfacts/cacao/util/NinePatchTexture.kt create mode 100644 src/main/kotlin/net/shadowfacts/cacao/view/NinePatchView.kt diff --git a/src/main/kotlin/net/shadowfacts/asmr/TestCacaoScreen.kt b/src/main/kotlin/net/shadowfacts/asmr/TestCacaoScreen.kt index 61ae588..3db231c 100644 --- a/src/main/kotlin/net/shadowfacts/asmr/TestCacaoScreen.kt +++ b/src/main/kotlin/net/shadowfacts/asmr/TestCacaoScreen.kt @@ -3,15 +3,13 @@ package net.shadowfacts.asmr import net.minecraft.util.Identifier import net.shadowfacts.kiwidsl.dsl import net.shadowfacts.cacao.CacaoScreen -import net.shadowfacts.cacao.view.View import net.shadowfacts.cacao.Window import net.shadowfacts.cacao.geometry.Axis import net.shadowfacts.cacao.geometry.Size import net.shadowfacts.cacao.util.Color +import net.shadowfacts.cacao.util.NinePatchTexture import net.shadowfacts.cacao.util.Texture -import net.shadowfacts.cacao.view.Label -import net.shadowfacts.cacao.view.StackView -import net.shadowfacts.cacao.view.TextureView +import net.shadowfacts.cacao.view.* import net.shadowfacts.cacao.view.button.Button /** @@ -27,21 +25,21 @@ class TestCacaoScreen: CacaoScreen() { val red = stack.addArrangedSubview(TextureView(Texture(Identifier("textures/block/birch_log_top.png"), 0, 0, 16, 16)).apply { intrinsicContentSize = Size(50.0, 50.0) }) - val green = stack.addArrangedSubview(View().apply { + val buttonNinePatch = NinePatchTexture(Texture(Identifier("textures/gui/widgets.png"), 0, 66), 3, 3, 194, 14) + val green = stack.addArrangedSubview(NinePatchView(buttonNinePatch).apply { intrinsicContentSize = Size(75.0, 100.0) - backgroundColor = Color(0x00ff00) }) val blue = stack.addArrangedSubview(View().apply { intrinsicContentSize = Size(50.0, 50.0) backgroundColor = Color(0x0000ff) }) - val purple = blue.addSubview(Button(Label("Hello, button!")).apply { + val purple = blue.addSubview(Button(Label("Hello, button!").apply { + textColor = Color.WHITE + }).apply { handler = { println("$it clicked!") } - background = View().apply { - backgroundColor = Color(0xebfc00) - } + background = NinePatchView(buttonNinePatch) }) solver.dsl { diff --git a/src/main/kotlin/net/shadowfacts/cacao/util/NinePatchTexture.kt b/src/main/kotlin/net/shadowfacts/cacao/util/NinePatchTexture.kt new file mode 100644 index 0000000..89dea70 --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/cacao/util/NinePatchTexture.kt @@ -0,0 +1,51 @@ +package net.shadowfacts.cacao.util + +/** + * Helper class that represents a texture that can be divided into nine pieces (4 corners, 4 edges, and the center) + * and can be drawn at any size by combining and repeating those pieces. + * + * It also provides convenience [Texture] objects that represent the different patches. + * + * @author shadowfacts + * @param texture The base [Texture] object. + * @param cornerWidth The width of each corner (and therefore the width of the vertical edges). + * @param cornerHeight The height of each corner (and therefore the height of the horizontal edges.) + * @param centerWidth The width of the center patch. + * @param centerHeight The height of the center patch. + */ +data class NinePatchTexture(val texture: Texture, val cornerWidth: Int, val cornerHeight: Int, val centerWidth: Int, val centerHeight: Int) { + + // Corners + val topLeft by lazy { + texture + } + val topRight by lazy { + Texture(texture.location, texture.u + cornerWidth + centerWidth, texture.v, texture.width, texture.height) + } + val bottomLeft by lazy { + Texture(texture.location, texture.u, texture.v + cornerHeight + centerHeight, texture.width, texture.height) + } + val bottomRight by lazy { + Texture(texture.location, topRight.u, bottomLeft.v, texture.width, texture.height) + } + + // Edges + val topMiddle by lazy { + Texture(texture.location, texture.u + cornerWidth, texture.v, texture.width, texture.height) + } + val bottomMiddle by lazy { + Texture(texture.location, topMiddle.u, bottomLeft.v, texture.width, texture.height) + } + val leftMiddle by lazy { + Texture(texture.location, texture.u, texture.v + cornerHeight, texture.width, texture.height) + } + val rightMiddle by lazy { + Texture(texture.location, topRight.u, leftMiddle.v, texture.width, texture.height) + } + + // Center + val center by lazy { + Texture(texture.location, texture.u + cornerWidth, texture.v + cornerHeight, texture.width, texture.height) + } + +} diff --git a/src/main/kotlin/net/shadowfacts/cacao/util/RenderHelper.kt b/src/main/kotlin/net/shadowfacts/cacao/util/RenderHelper.kt index 17cb67d..83e13a0 100644 --- a/src/main/kotlin/net/shadowfacts/cacao/util/RenderHelper.kt +++ b/src/main/kotlin/net/shadowfacts/cacao/util/RenderHelper.kt @@ -3,7 +3,6 @@ package net.shadowfacts.cacao.util import com.mojang.blaze3d.platform.GlStateManager import net.minecraft.client.MinecraftClient import net.minecraft.client.gui.DrawableHelper -import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.geometry.Rect /** @@ -25,15 +24,21 @@ object RenderHelper { } /** - * Draws the given [texture] filling the [rect]. + * Binds and draws the given [texture] filling the [rect]. */ fun draw(rect: Rect, texture: Texture) { if (disabled) return color(1f, 1f, 1f, 1f) MinecraftClient.getInstance().textureManager.bindTexture(texture.location) - val u = texture.u / texture.width.toFloat() - val v = texture.v / texture.height.toFloat() - DrawableHelper.blit(rect.left.toInt(), rect.top.toInt(), u, v, rect.width.toInt(), rect.height.toInt(), texture.width, texture.height) + draw(rect.left, rect.top, texture.u, texture.v, rect.width, rect.height, texture.width, texture.height) + } + + /** + * Draws the bound texture with the given screen and texture position and size. + */ + fun draw(x: Double, y: Double, u: Int, v: Int, width: Double, height: Double, textureWidth: Int, textureHeight: Int) { + if (disabled) return + DrawableHelper.blit(x.toInt(), y.toInt(), u.toFloat(), v.toFloat(), width.toInt(), height.toInt(), textureWidth, textureHeight) } /** diff --git a/src/main/kotlin/net/shadowfacts/cacao/view/NinePatchView.kt b/src/main/kotlin/net/shadowfacts/cacao/view/NinePatchView.kt new file mode 100644 index 0000000..71f125b --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/cacao/view/NinePatchView.kt @@ -0,0 +1,107 @@ +package net.shadowfacts.cacao.view + +import net.shadowfacts.cacao.geometry.Point +import net.shadowfacts.cacao.geometry.Rect +import net.shadowfacts.cacao.util.NinePatchTexture +import net.shadowfacts.cacao.util.RenderHelper + +/** + * A helper class for drawing a [NinePatchTexture] in a view. + * `NinePatchView` will draw the given nine patch texture filling its bounds. + * + * @author shadowfacts + * @param ninePatch The nine patch texture that this view will draw. + */ +class NinePatchView(val ninePatch: NinePatchTexture): View() { + + // Corners + private val topLeft: Rect by lazy { + Rect(0.0, 0.0, ninePatch.cornerWidth.toDouble(), ninePatch.cornerHeight.toDouble()) + } + private val topRight by lazy { + Rect(bounds.width - ninePatch.cornerWidth, 0.0, ninePatch.cornerWidth.toDouble(), ninePatch.cornerHeight.toDouble()) + } + private val bottomLeft by lazy { + Rect(0.0, bounds.height - ninePatch.cornerHeight, ninePatch.cornerWidth.toDouble(), ninePatch.cornerHeight.toDouble()) + } + private val bottomRight by lazy { + Rect(bounds.width - ninePatch.cornerWidth, bounds.height - ninePatch.cornerHeight, ninePatch.cornerWidth.toDouble(), ninePatch.cornerHeight.toDouble()) + } + + // Edges + private val topMiddle by lazy { + Rect(ninePatch.cornerWidth.toDouble(), topLeft.top, bounds.width - 2 * ninePatch.cornerWidth, ninePatch.cornerHeight.toDouble()) + } + private val bottomMiddle by lazy { + Rect(topMiddle.left, bottomLeft.top, topMiddle.width, topMiddle.height) + } + private val leftMiddle by lazy { + Rect(topLeft.left, ninePatch.cornerHeight.toDouble(), ninePatch.cornerWidth.toDouble(), bounds.height - 2 * ninePatch.cornerHeight) + } + private val rightMiddle by lazy { + Rect(topRight.left, leftMiddle.top, leftMiddle.width, leftMiddle.height) + } + + // Center + private val center by lazy { + Rect(topLeft.right, topLeft.bottom, topMiddle.width, leftMiddle.height) + } + + override fun drawContent(mouse: Point, delta: Float) { + drawCorners() + drawEdges() + drawCenter() + } + + private fun drawCorners() { + RenderHelper.draw(topLeft, ninePatch.topLeft) + RenderHelper.draw(topRight, ninePatch.topRight) + RenderHelper.draw(bottomLeft, ninePatch.bottomLeft) + RenderHelper.draw(bottomRight, ninePatch.bottomRight) + } + + private fun drawEdges() { + // Horizontal + for (i in 0 until (topMiddle.width.toInt() / ninePatch.centerWidth)) { + RenderHelper.draw(topMiddle.left + i * ninePatch.centerWidth, topMiddle.top, ninePatch.topMiddle.u, ninePatch.topMiddle.v, ninePatch.centerWidth.toDouble(), ninePatch.cornerHeight.toDouble(), ninePatch.texture.width, ninePatch.texture.height) + RenderHelper.draw(bottomMiddle.left + i * ninePatch.centerWidth, bottomMiddle.top, ninePatch.bottomMiddle.u, ninePatch.bottomMiddle.v, ninePatch.centerWidth.toDouble(), ninePatch.cornerHeight.toDouble(), ninePatch.texture.width, ninePatch.texture.height) + } + val remWidth = topMiddle.width.toInt() % ninePatch.centerWidth + if (remWidth > 0) { + RenderHelper.draw(topMiddle.right - remWidth, topMiddle.top, ninePatch.topMiddle.u, ninePatch.topMiddle.v, remWidth.toDouble(), ninePatch.cornerHeight.toDouble(), ninePatch.texture.width, ninePatch.texture.height) + RenderHelper.draw(bottomMiddle.right - remWidth, bottomMiddle.top, ninePatch.bottomMiddle.u, ninePatch.bottomMiddle.v, remWidth.toDouble(), ninePatch.cornerHeight.toDouble(), ninePatch.texture.width, ninePatch.texture.height) + } + + // Vertical + for (i in 0 until (leftMiddle.height.toInt() / ninePatch.centerHeight)) { + RenderHelper.draw(leftMiddle.left, leftMiddle.top + i * ninePatch.centerHeight, ninePatch.leftMiddle.u, ninePatch.leftMiddle.v, ninePatch.cornerWidth.toDouble(), ninePatch.centerHeight.toDouble(), ninePatch.texture.width, ninePatch.texture.height) + RenderHelper.draw(rightMiddle.left, rightMiddle.top + i * ninePatch.centerHeight, ninePatch.rightMiddle.u, ninePatch.rightMiddle.v, ninePatch.cornerWidth.toDouble(), ninePatch.centerHeight.toDouble(), ninePatch.texture.width, ninePatch.texture.height) + } + val remHeight = leftMiddle.height.toInt() % ninePatch.centerHeight + if (remHeight > 0) { + RenderHelper.draw(leftMiddle.left, leftMiddle.bottom - remHeight, ninePatch.leftMiddle.u, ninePatch.leftMiddle.v, ninePatch.cornerWidth.toDouble(), remHeight.toDouble(), ninePatch.texture.width, ninePatch.texture.height) + RenderHelper.draw(rightMiddle.left, rightMiddle.bottom - remHeight, ninePatch.rightMiddle.u, ninePatch.rightMiddle.v, ninePatch.cornerWidth.toDouble(), remHeight.toDouble(), ninePatch.texture.width, ninePatch.texture.height) + } + } + + private fun drawCenter() { + for (i in 0 until (center.height.toInt() / ninePatch.centerHeight)) { + drawCenterRow(center.top + i * ninePatch.centerHeight.toDouble(), ninePatch.centerHeight.toDouble()) + } + val remHeight = center.height.toInt() % ninePatch.centerHeight + if (remHeight > 0) { + drawCenterRow(center.bottom - remHeight, remHeight.toDouble()) + } + } + + private fun drawCenterRow(y: Double, height: Double) { + for (i in 0 until (center.width.toInt() / ninePatch.centerWidth)) { + RenderHelper.draw(center.left + i * ninePatch.centerWidth, y, ninePatch.center.u, ninePatch.center.v, ninePatch.centerWidth.toDouble(), height, ninePatch.texture.width, ninePatch.texture.height) + } + val remWidth = center.width.toInt() % ninePatch.centerWidth + if (remWidth > 0) { + RenderHelper.draw(center.right - remWidth, y, ninePatch.center.u, ninePatch.center.v, remWidth.toDouble(), height, ninePatch.texture.width, ninePatch.texture.height) + } + } + +} \ No newline at end of file