Add NinePatchTexture and NinePatchView

This commit is contained in:
Shadowfacts 2019-06-23 19:58:06 -04:00
parent 6485fd1035
commit c79b93a4f9
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
4 changed files with 176 additions and 15 deletions

View File

@ -3,15 +3,13 @@ package net.shadowfacts.asmr
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.shadowfacts.kiwidsl.dsl import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.cacao.CacaoScreen import net.shadowfacts.cacao.CacaoScreen
import net.shadowfacts.cacao.view.View
import net.shadowfacts.cacao.Window import net.shadowfacts.cacao.Window
import net.shadowfacts.cacao.geometry.Axis import net.shadowfacts.cacao.geometry.Axis
import net.shadowfacts.cacao.geometry.Size import net.shadowfacts.cacao.geometry.Size
import net.shadowfacts.cacao.util.Color import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.util.NinePatchTexture
import net.shadowfacts.cacao.util.Texture import net.shadowfacts.cacao.util.Texture
import net.shadowfacts.cacao.view.Label import net.shadowfacts.cacao.view.*
import net.shadowfacts.cacao.view.StackView
import net.shadowfacts.cacao.view.TextureView
import net.shadowfacts.cacao.view.button.Button 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 { val red = stack.addArrangedSubview(TextureView(Texture(Identifier("textures/block/birch_log_top.png"), 0, 0, 16, 16)).apply {
intrinsicContentSize = Size(50.0, 50.0) 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) intrinsicContentSize = Size(75.0, 100.0)
backgroundColor = Color(0x00ff00)
}) })
val blue = stack.addArrangedSubview(View().apply { val blue = stack.addArrangedSubview(View().apply {
intrinsicContentSize = Size(50.0, 50.0) intrinsicContentSize = Size(50.0, 50.0)
backgroundColor = Color(0x0000ff) 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 = { handler = {
println("$it clicked!") println("$it clicked!")
} }
background = View().apply { background = NinePatchView(buttonNinePatch)
backgroundColor = Color(0xebfc00)
}
}) })
solver.dsl { solver.dsl {

View File

@ -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)
}
}

View File

@ -3,7 +3,6 @@ package net.shadowfacts.cacao.util
import com.mojang.blaze3d.platform.GlStateManager import com.mojang.blaze3d.platform.GlStateManager
import net.minecraft.client.MinecraftClient import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.DrawableHelper import net.minecraft.client.gui.DrawableHelper
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Rect 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) { fun draw(rect: Rect, texture: Texture) {
if (disabled) return if (disabled) return
color(1f, 1f, 1f, 1f) color(1f, 1f, 1f, 1f)
MinecraftClient.getInstance().textureManager.bindTexture(texture.location) MinecraftClient.getInstance().textureManager.bindTexture(texture.location)
val u = texture.u / texture.width.toFloat() draw(rect.left, rect.top, texture.u, texture.v, rect.width, rect.height, texture.width, texture.height)
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)
/**
* 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)
} }
/** /**

View File

@ -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)
}
}
}