Convert Terminal screen to Cacao

This commit is contained in:
Shadowfacts 2021-03-20 22:31:53 -04:00
parent 2774cabfcc
commit 3e66d7439a
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
15 changed files with 540 additions and 425 deletions

View File

@ -19,8 +19,10 @@ object PhyConPlugin: REIPluginV0 {
override fun registerBounds(helper: DisplayHelper) {
BaseBoundsHandler.getInstance().registerExclusionZones(TerminalScreen::class.java) {
val screen = MinecraftClient.getInstance().currentScreen as TerminalScreen
val button = screen.terminalVC.sortMode
val rect = button.convert(button.bounds, to = null)
listOf(
Rectangle(screen.sortButtonX, screen.sortButtonY, 20, 20)
Rectangle(rect.left.toInt(), rect.top.toInt(), 20, 20)
)
}
}

View File

@ -1,5 +1,6 @@
package net.shadowfacts.cacao
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.gui.screen.ingame.HandledScreen
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.entity.player.PlayerInventory
@ -68,7 +69,6 @@ open class CacaoHandledScreen<Handler: ScreenHandler>(
}
override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) {
renderBackground(matrixStack)
}
override fun drawForeground(matrixStack: MatrixStack, mouseX: Int, mouseY: Int) {
@ -77,15 +77,28 @@ open class CacaoHandledScreen<Handler: ScreenHandler>(
override fun render(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
val mouse = Point(mouseX, mouseY)
windows.forEachIndexed { index, it ->
RenderSystem.pushMatrix()
RenderSystem.translatef(0f, 0f, -350f)
for (i in windows.indices) {
val it = windows[i]
if (i == windows.size - 1) {
renderBackground(matrixStack)
}
if (it is ScreenHandlerWindow) {
if (index == windows.size - 1) {
if (i == windows.size - 1) {
super.render(matrixStack, mouseX, mouseY, delta)
} else {
// if the screen handler window is not the frontmost, we fake the mouse x/y to disable the slot mouseover effect
super.render(matrixStack, -1, -1, delta)
}
RenderSystem.popMatrix()
}
it.draw(matrixStack, mouse, delta)
}

View File

@ -6,6 +6,7 @@ import net.shadowfacts.cacao.geometry.Rect
import net.shadowfacts.cacao.util.texture.NinePatchTexture
import net.shadowfacts.cacao.util.RenderHelper
import net.shadowfacts.cacao.util.properties.ResettableLazyProperty
import kotlin.math.roundToInt
/**
* A helper class for drawing a [NinePatchTexture] in a view.
@ -91,22 +92,22 @@ open class NinePatchView(val ninePatch: NinePatchTexture): View() {
private fun drawEdges(matrixStack: MatrixStack) {
// Horizontal
for (i in 0 until (topMiddle.width.toInt() / ninePatch.centerWidth)) {
for (i in 0 until (topMiddle.width.roundToInt() / ninePatch.centerWidth)) {
RenderHelper.draw(matrixStack, topMiddle.left + i * ninePatch.centerWidth, topMiddle.top, ninePatch.topMiddle.u, ninePatch.topMiddle.v, ninePatch.centerWidth.toDouble(), topMiddle.height, ninePatch.texture.width, ninePatch.texture.height)
RenderHelper.draw(matrixStack, bottomMiddle.left + i * ninePatch.centerWidth, bottomMiddle.top, ninePatch.bottomMiddle.u, ninePatch.bottomMiddle.v, ninePatch.centerWidth.toDouble(), bottomMiddle.height, ninePatch.texture.width, ninePatch.texture.height)
}
val remWidth = topMiddle.width.toInt() % ninePatch.centerWidth
val remWidth = topMiddle.width.roundToInt() % ninePatch.centerWidth
if (remWidth > 0) {
RenderHelper.draw(matrixStack, topMiddle.right - remWidth, topMiddle.top, ninePatch.topMiddle.u, ninePatch.topMiddle.v, remWidth.toDouble(), ninePatch.cornerHeight.toDouble(), ninePatch.texture.width, ninePatch.texture.height)
RenderHelper.draw(matrixStack, 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)) {
for (i in 0 until (leftMiddle.height.roundToInt() / ninePatch.centerHeight)) {
RenderHelper.draw(matrixStack, 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(matrixStack, 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
val remHeight = leftMiddle.height.roundToInt() % ninePatch.centerHeight
if (remHeight > 0) {
RenderHelper.draw(matrixStack, leftMiddle.left, leftMiddle.bottom - remHeight, ninePatch.leftMiddle.u, ninePatch.leftMiddle.v, ninePatch.cornerWidth.toDouble(), remHeight.toDouble(), ninePatch.texture.width, ninePatch.texture.height)
RenderHelper.draw(matrixStack, rightMiddle.left, rightMiddle.bottom - remHeight, ninePatch.rightMiddle.u, ninePatch.rightMiddle.v, ninePatch.cornerWidth.toDouble(), remHeight.toDouble(), ninePatch.texture.width, ninePatch.texture.height)
@ -114,20 +115,20 @@ open class NinePatchView(val ninePatch: NinePatchTexture): View() {
}
private fun drawCenter(matrixStack: MatrixStack) {
for (i in 0 until (center.height.toInt() / ninePatch.centerHeight)) {
for (i in 0 until (center.height.roundToInt() / ninePatch.centerHeight)) {
drawCenterRow(matrixStack, center.top + i * ninePatch.centerHeight.toDouble(), ninePatch.centerHeight.toDouble())
}
val remHeight = center.height.toInt() % ninePatch.centerHeight
val remHeight = center.height.roundToInt() % ninePatch.centerHeight
if (remHeight > 0) {
drawCenterRow(matrixStack, center.bottom - remHeight, remHeight.toDouble())
}
}
private fun drawCenterRow(matrixStack: MatrixStack, y: Double, height: Double) {
for (i in 0 until (center.width.toInt() / ninePatch.centerWidth)) {
for (i in 0 until (center.width.roundToInt() / ninePatch.centerWidth)) {
RenderHelper.draw(matrixStack, 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
val remWidth = center.width.roundToInt() % ninePatch.centerWidth
if (remWidth > 0) {
RenderHelper.draw(matrixStack, center.right - remWidth, y, ninePatch.center.u, ninePatch.center.v, remWidth.toDouble(), height, ninePatch.texture.width, ninePatch.texture.height)
}

View File

@ -41,7 +41,9 @@ class EnumButton<E: Enum<E>>(
value = when (mouseButton) {
MouseButton.LEFT -> EnumHelper.next(value)
MouseButton.RIGHT -> EnumHelper.previous(value)
else -> value
else -> {
return false
}
}
}

View File

@ -1,6 +1,7 @@
package net.shadowfacts.cacao.view.textfield
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.screen.TickableElement
import net.minecraft.client.gui.widget.TextFieldWidget
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.LiteralText
@ -24,7 +25,7 @@ import org.lwjgl.glfw.GLFW
*/
abstract class AbstractTextField<Impl: AbstractTextField<Impl>>(
initialText: String
): View() {
): View(), TickableElement {
/**
* A function that is invoked when the text in this text field changes.
@ -163,6 +164,10 @@ abstract class AbstractTextField<Impl: AbstractTextField<Impl>>(
return result || (isFirstResponder && keyCode != GLFW.GLFW_KEY_ESCAPE)
}
override fun tick() {
minecraftWidget.tick()
}
// todo: label for the TextFieldWidget?
private class ProxyWidget: TextFieldWidget(MinecraftClient.getInstance().textRenderer, 0, 0, 0, 0, LiteralText("")) {
// AbstractButtonWidget.height is protected

View File

@ -3,7 +3,7 @@ package net.shadowfacts.cacao.view.textfield
/**
* @author shadowfacts
*/
class NumberField(
open class NumberField(
initialValue: Int,
handler: ((NumberField) -> Unit)? = null,
): AbstractTextField<NumberField>(initialValue.toString()) {

View File

@ -7,7 +7,7 @@ package net.shadowfacts.cacao.view.textfield
* @param initialText The initial value of this text field.
* @param handler A function that is invoked when the value of the text field changes.
*/
class TextField(
open class TextField(
initialText: String,
handler: ((TextField) -> Unit)? = null
): AbstractTextField<TextField>(initialText) {

View File

@ -129,10 +129,9 @@ class RedstoneEmitterScreen(
inv.leftAnchor equalTo (minX + 8)
inv.topAnchor equalTo (minY + 72)
// offset by 1 on each edge to account for MC border
field.widthAnchor equalTo 82
field.heightAnchor equalTo 11
field.leftAnchor equalTo (minX + 56)
field.leftAnchor equalTo (minX + 57)
field.topAnchor equalTo (minY + 23)
hStack.centerXAnchor equalTo view.centerXAnchor

View File

@ -0,0 +1,75 @@
package net.shadowfacts.phycon.block.terminal
import net.minecraft.util.Identifier
import net.minecraft.util.math.MathHelper
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Rect
import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.texture.Texture
import net.shadowfacts.cacao.view.TextureView
import net.shadowfacts.cacao.view.View
import net.shadowfacts.phycon.PhysicalConnectivity
/**
* @author shadowfacts
*/
class ScrollTrackView(
val scrollPositionChanged: (ScrollTrackView) -> Unit,
): View() {
companion object {
private val THUMB = Texture(Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal.png"), 52, 230)
private const val THUMB_WIDTH = 12.0
private const val THUMB_HEIGHT = 15.0
}
private lateinit var thumbView: TextureView
private var grabbedY: Double? = null
/**
* The [0,1] normalized position of the scroll thumb.
*/
var scrollPosition: Double
get() = thumbView.frame.top / (frame.height - THUMB_HEIGHT)
set(value) {
val newTop = MathHelper.clamp(value, 0.0, 1.0) * (frame.height - THUMB_HEIGHT)
thumbView.frame = Rect(0.0, newTop, THUMB_WIDTH, THUMB_HEIGHT)
}
init {
respondsToDragging = true
}
override fun wasAdded() {
super.wasAdded()
thumbView = addSubview(TextureView(THUMB).apply {
usesConstraintBasedLayout = false
frame = Rect(0.0, 0.0, THUMB_WIDTH, THUMB_HEIGHT)
})
}
override fun mouseDragged(point: Point, delta: Point, mouseButton: MouseButton): Boolean {
if (grabbedY == null && point !in thumbView.frame) {
val newCenter = MathHelper.clamp(point.y, THUMB_HEIGHT / 2, frame.height - THUMB_HEIGHT / 2)
thumbView.frame = Rect(0.0, newCenter - THUMB_HEIGHT / 2, THUMB_WIDTH, THUMB_HEIGHT)
}
if (grabbedY == null) {
grabbedY = point.y - thumbView.frame.top
}
val newTop = MathHelper.clamp(point.y - grabbedY!!, 0.0, frame.height - THUMB_HEIGHT)
thumbView.frame = Rect(0.0, newTop, THUMB_WIDTH, THUMB_HEIGHT)
scrollPositionChanged(this)
return true
}
override fun mouseDragEnded(point: Point, mouseButton: MouseButton) {
grabbedY = null
}
}

View File

@ -0,0 +1,57 @@
package net.shadowfacts.phycon.block.terminal
import net.minecraft.util.Identifier
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.geometry.Size
import net.shadowfacts.cacao.util.EnumHelper
import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.texture.Texture
import net.shadowfacts.cacao.view.TextureView
import net.shadowfacts.cacao.view.button.AbstractButton
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.util.SortMode
/**
* @author shadowfacts
*/
class SortModeButton(
initialValue: SortMode,
): AbstractButton<SortModeButton>(
TextureView(TEXTURES[initialValue]!!).apply {
intrinsicContentSize = Size(16.0, 16.0)
}
) {
companion object {
private val ID = Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal.png")
private val TEXTURES = mapOf(
SortMode.COUNT_HIGH_FIRST to Texture(ID, 0, 230),
SortMode.COUNT_LOW_FIRST to Texture(ID, 16, 230),
SortMode.ALPHABETICAL to Texture(ID, 32, 230),
)
}
private val textureView: TextureView
get() = content as TextureView
var value: SortMode = initialValue
set(value) {
field = value
textureView.texture = TEXTURES[value]!!
}
override fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean {
if (!disabled) {
value = when (mouseButton) {
MouseButton.LEFT -> EnumHelper.next(value)
MouseButton.RIGHT -> EnumHelper.previous(value)
else -> {
return false
}
}
}
return super.mouseClicked(point, mouseButton)
}
}

View File

@ -0,0 +1,152 @@
package net.shadowfacts.phycon.block.terminal
import net.minecraft.item.ItemStack
import net.minecraft.util.Identifier
import net.shadowfacts.cacao.util.KeyModifiers
import net.shadowfacts.cacao.util.texture.Texture
import net.shadowfacts.cacao.view.Label
import net.shadowfacts.cacao.view.TextureView
import net.shadowfacts.cacao.view.button.Button
import net.shadowfacts.cacao.view.textfield.NumberField
import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.PhysicalConnectivity
import org.lwjgl.glfw.GLFW
import kotlin.math.ceil
import kotlin.math.floor
/**
* @author shadowfacts
*/
class TerminalRequestAmountViewController(
val screen: TerminalScreen,
val stack: ItemStack,
): ViewController() {
companion object {
private val BACKGROUND = Texture(Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal_amount.png"), 0, 0)
}
lateinit var field: NumberField
private set
override fun viewDidLoad() {
super.viewDidLoad()
val pane = view.addLayoutGuide()
view.solver.dsl {
pane.widthAnchor equalTo 158
pane.heightAnchor equalTo 62
pane.centerXAnchor equalTo view.centerXAnchor
pane.centerYAnchor equalTo view.centerYAnchor
}
val background = view.addSubview(TextureView(BACKGROUND)).apply {
zIndex = -1.0
}
field = view.addSubview(AmountField(this)).apply {
drawBackground = false
}
field.becomeFirstResponder()
val requestLabel = Label("Request", shadow = true)
val request = view.addSubview(Button(requestLabel) {
doRequest()
})
val plusOneLabel = Label("+1", shadow = true)
val plusOne = view.addSubview(Button(plusOneLabel) {
field.number = (field.number ?: 1) + 1
})
val plusTenLabel = Label("+10", shadow = true)
val plusTen = view.addSubview(Button(plusTenLabel) {
val old = field.number ?: 1
field.number = ceil((old + 1) / 10.0).toInt() * 10
})
val plusHundredLabel = Label("+100", shadow = true)
val plusHundred = view.addSubview(Button(plusHundredLabel) {
val old = field.number ?: 1
field.number = ceil((old + 1) / 100.0).toInt() * 100
})
val minusOneLabel = Label("-1", shadow = true)
val minusOne = view.addSubview(Button(minusOneLabel) {
field.number = (field.number ?: 1) - 1
})
val minusTenLabel = Label("-10", shadow = true)
val minusTen = view.addSubview(Button(minusTenLabel) {
val old = field.number ?: 1
field.number = floor((old - 1) / 10.0).toInt() * 10
})
val minusHundredLabel = Label("-100", shadow = true)
val minusHundred = view.addSubview(Button(minusHundredLabel) {
val old = field.number ?: 1
field.number = floor((old - 1) / 100.0).toInt() * 100
})
view.solver.dsl {
background.leftAnchor equalTo pane.leftAnchor
background.rightAnchor equalTo pane.rightAnchor
background.topAnchor equalTo pane.topAnchor
background.bottomAnchor equalTo pane.bottomAnchor
field.leftAnchor equalTo (pane.leftAnchor + 8)
field.topAnchor equalTo (pane.topAnchor + 27)
field.widthAnchor equalTo 80
field.heightAnchor equalTo 9
request.leftAnchor equalTo (pane.leftAnchor + 101)
request.centerYAnchor equalTo field.centerYAnchor
request.widthAnchor equalTo 50
request.heightAnchor equalTo 20
plusOne.leftAnchor equalTo (pane.leftAnchor + 7)
plusTen.leftAnchor equalTo (plusOne.rightAnchor + 3)
plusHundred.leftAnchor equalTo (plusTen.rightAnchor + 3)
plusHundred.rightAnchor equalTo (pane.leftAnchor + 97)
plusOne.widthAnchor equalTo plusTen.widthAnchor
plusOne.widthAnchor equalTo plusHundred.widthAnchor
plusOne.topAnchor equalTo (pane.topAnchor + 7)
plusTen.topAnchor equalTo (pane.topAnchor + 7)
plusHundred.topAnchor equalTo (pane.topAnchor + 7)
plusOne.heightAnchor equalTo 14
plusTen.heightAnchor equalTo 14
plusHundred.heightAnchor equalTo 14
minusOne.leftAnchor equalTo (pane.leftAnchor + 7)
minusTen.leftAnchor equalTo (minusOne.rightAnchor + 3)
minusHundred.leftAnchor equalTo (minusTen.rightAnchor + 3)
minusHundred.rightAnchor equalTo (pane.leftAnchor + 97)
minusOne.widthAnchor equalTo minusTen.widthAnchor
minusOne.widthAnchor equalTo minusHundred.widthAnchor
minusOne.topAnchor equalTo (pane.topAnchor + 41)
minusTen.topAnchor equalTo (pane.topAnchor + 41)
minusHundred.topAnchor equalTo (pane.topAnchor + 41)
minusOne.heightAnchor equalTo 14
minusTen.heightAnchor equalTo 14
minusHundred.heightAnchor equalTo 14
}
}
private fun doRequest() {
screen.requestItem(stack, field.number ?: 1)
window!!.removeFromScreen()
}
class AmountField(val vc: TerminalRequestAmountViewController): NumberField(1) {
override fun keyPressed(keyCode: Int, modifiers: KeyModifiers): Boolean {
return if (super.keyPressed(keyCode, modifiers)) {
true
} else if (keyCode == GLFW.GLFW_KEY_ENTER) {
vc.doRequest()
true
} else {
false
}
}
}
}

View File

@ -3,10 +3,8 @@ package net.shadowfacts.phycon.block.terminal
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.DrawableHelper
import net.minecraft.client.gui.screen.ingame.HandledScreen
import net.minecraft.client.gui.widget.AbstractButtonWidget
import net.minecraft.client.gui.widget.AbstractPressableButtonWidget
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.gui.Element
import net.minecraft.client.gui.screen.TickableElement
import net.minecraft.client.gui.widget.TextFieldWidget
import net.minecraft.client.render.Tessellator
import net.minecraft.client.render.VertexConsumerProvider
@ -17,235 +15,67 @@ import net.minecraft.screen.slot.Slot
import net.minecraft.screen.slot.SlotActionType
import net.minecraft.text.LiteralText
import net.minecraft.text.Text
import net.minecraft.text.TranslatableText
import net.minecraft.util.Identifier
import net.minecraft.util.math.MathHelper
import net.shadowfacts.cacao.CacaoHandledScreen
import net.shadowfacts.cacao.window.ScreenHandlerWindow
import net.shadowfacts.cacao.window.Window
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.networking.C2STerminalRequestItem
import net.shadowfacts.phycon.networking.C2STerminalUpdateDisplayedItems
import net.shadowfacts.phycon.util.SortMode
import net.shadowfacts.phycon.util.next
import net.shadowfacts.phycon.util.prev
import org.lwjgl.glfw.GLFW
import java.lang.NumberFormatException
import java.math.RoundingMode
import java.text.DecimalFormat
import java.util.LinkedList
import kotlin.math.ceil
import kotlin.math.floor
import kotlin.math.min
import kotlin.math.roundToInt
/**
* @author shadowfacts
*/
// todo: translate title
class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, title: Text): HandledScreen<TerminalScreenHandler>(handler, playerInv, title) {
class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, title: Text): CacaoHandledScreen<TerminalScreenHandler>(handler, playerInv, title) {
companion object {
private val BACKGROUND = Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal.png")
private val DIALOG = Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal_amount.png")
}
private lateinit var searchBox: TextFieldWidget
private lateinit var sortButton: SortButton
var sortButtonX: Int = 0
private set
var sortButtonY = 0
private set
private lateinit var amountBox: TextFieldWidget
private var dialogStack = ItemStack.EMPTY
private var showingAmountDialog = false
set(value) {
val oldValue = field
field = value
for (e in dialogChildren) {
e.visible = value
}
amountBox.isVisible
searchBox.setSelected(!value)
amountBox.setSelected(value)
if (value && !oldValue) {
amountBox.text = "1"
}
updateFocusedElement()
}
private var dialogChildren = mutableListOf<AbstractButtonWidget>()
val backgroundWidth: Int
get() = backgroundWidth
val backgroundHeight: Int
get() = backgroundHeight
private var scrollPosition = 0f
private var isDraggingScrollThumb = false
private val trackMinY = 18
private val trackHeight = 106
private val thumbHeight = 15
private val thumbWidth = 12
private val scrollThumbTop: Int
get() = trackMinY + (scrollPosition * (trackHeight - thumbHeight)).roundToInt()
// val tickableElements = LinkedList<TickableElement>()
private val dialogWidth = 158
private val dialogHeight = 62
val terminalVC = TerminalViewController(this, handler, handler.terminal)
var amountVC: TerminalRequestAmountViewController? = null
var searchQuery = ""
var scrollPosition = 0.0
var sortMode = SortMode.COUNT_HIGH_FIRST
init {
backgroundWidth = 252
backgroundHeight = 222
}
override fun init() {
super.init()
addWindow(ScreenHandlerWindow(handler, terminalVC))
children.clear()
dialogChildren.clear()
client!!.keyboard.setRepeatEvents(true)
searchBox = TextFieldWidget(textRenderer, x + 138, y + 6, 80, 9, LiteralText("Search"))
searchBox.setMaxLength(50)
// setHasBorder is actually setDrawsBackground
searchBox.setHasBorder(false)
searchBox.isVisible = true
searchBox.setSelected(true)
searchBox.setEditableColor(0xffffff)
addChild(searchBox)
sortButtonX = x + 256
sortButtonY = y
sortButton = SortButton(sortButtonX, sortButtonY, handler.sortMode, {
requestUpdatedItems()
}, ::renderTooltip)
addButton(sortButton)
val dialogMinX = width / 2 - dialogWidth / 2
val dialogMinY = height / 2 - dialogHeight / 2
amountBox = TextFieldWidget(textRenderer, dialogMinX + 8, dialogMinY + 27, 80, 9, LiteralText("Amount"))
amountBox.setHasBorder(false)
amountBox.isVisible = false
amountBox.setSelected(false)
amountBox.setEditableColor(0xffffff)
amountBox.setTextPredicate {
if (it.isEmpty()) {
true
} else {
try {
Integer.parseInt(it) > 0
} catch (e: NumberFormatException) {
false
}
}
}
dialogChildren.add(amountBox)
val plusOne = SmallButton(dialogMinX + 7, dialogMinY + 7, 28, LiteralText("+1")) {
amountBox.intValue += 1
}
dialogChildren.add(plusOne)
val plusTen = SmallButton(dialogMinX + 7 + 28 + 3, dialogMinY + 7, 28, LiteralText("+10")) {
amountBox.intValue = ceil((amountBox.intValue + 1) / 10.0).toInt() * 10
}
dialogChildren.add(plusTen)
val plusHundred = SmallButton(dialogMinX + 7 + (28 + 3) * 2, dialogMinY + 7, 28, LiteralText("+100")) {
amountBox.intValue = ceil((amountBox.intValue + 1) / 100.0).toInt() * 100
}
dialogChildren.add(plusHundred)
val minusOne = SmallButton(dialogMinX + 7, dialogMinY + 39, 28, LiteralText("-1")) {
amountBox.intValue -= 1
}
dialogChildren.add(minusOne)
val minusTen = SmallButton(dialogMinX + 7 + 28 + 3, dialogMinY + 39, 28, LiteralText("-10")) {
amountBox.intValue = floor((amountBox.intValue - 1) / 10.0).toInt() * 10
}
dialogChildren.add(minusTen)
val minusHundred = SmallButton(dialogMinX + 7 + (28 + 3) * 2, dialogMinY + 39, 28, LiteralText("-100")) {
amountBox.intValue = floor((amountBox.intValue - 1) / 100.0).toInt() * 100
}
dialogChildren.add(minusHundred)
// 101,25
val request = ButtonWidget(dialogMinX + 101, dialogMinY + 21, 50, 20, LiteralText("Request")) {
doDialogRequest()
}
dialogChildren.add(request)
updateFocusedElement()
requestUpdatedItems()
}
private fun updateFocusedElement() {
focused = if (showingAmountDialog) {
amountBox
} else if (searchBox.isFocused) {
searchBox
} else {
null
}
fun requestItem(stack: ItemStack, amount: Int) {
val netHandler = MinecraftClient.getInstance().player!!.networkHandler
val packet = C2STerminalRequestItem(handler.terminal, stack, amount)
netHandler.sendPacket(packet)
}
private fun requestUpdatedItems() {
fun requestUpdatedItems() {
val player = MinecraftClient.getInstance().player!!
player.networkHandler.sendPacket(C2STerminalUpdateDisplayedItems(handler.terminal, searchBox.text, sortButton.mode, scrollPosition))
player.networkHandler.sendPacket(C2STerminalUpdateDisplayedItems(handler.terminal, searchQuery, sortMode, scrollPosition.toFloat()))
}
override fun tick() {
super.tick()
searchBox.tick()
amountBox.tick()
}
override fun drawForeground(matrixStack: MatrixStack, mouseX: Int, mouseY: Int) {
textRenderer.draw(matrixStack, title, 65f, 6f, 0x404040)
textRenderer.draw(matrixStack, playerInventory.displayName, 65f, backgroundHeight - 94f, 0x404040)
textRenderer.draw(matrixStack, TranslatableText("gui.phycon.terminal_buffer"), 7f, 6f, 0x404040)
}
override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) {
// if the dialog is open, the background gradient will be drawn in front of the main terminal gui
if (!showingAmountDialog) {
renderBackground(matrixStack)
}
RenderSystem.color4f(1f, 1f, 1f, 1f)
client!!.textureManager.bindTexture(BACKGROUND)
val x = (width - backgroundWidth) / 2
val y = (height - backgroundHeight) / 2
drawTexture(matrixStack, x, y, 0, 0, backgroundWidth, backgroundHeight)
// scroll thumb
drawTexture(matrixStack, x + 232, y + scrollThumbTop, 52, 230, thumbWidth, thumbHeight)
}
@ExperimentalUnsignedTypes
override fun render(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
if (showingAmountDialog) {
RenderSystem.pushMatrix()
// items are rendered at some stupidly high z offset. item amounts at an even higher one
RenderSystem.translatef(0f, 0f, -350f)
// fake the mouse x/y while showing a dialog so slot mouseover highlights aren't drawn
super.render(matrixStack, -1, -1, delta)
RenderSystem.popMatrix()
} else {
super.render(matrixStack, mouseX, mouseY, delta)
}
searchBox.render(matrixStack, mouseX, mouseY, delta)
if (showingAmountDialog) {
renderBackground(matrixStack)
RenderSystem.color4f(1f, 1f, 1f, 1f)
client!!.textureManager.bindTexture(DIALOG)
val dialogMinX = width / 2 - dialogWidth / 2
val dialogMinY = height / 2 - dialogHeight / 2
drawTexture(matrixStack, dialogMinX, dialogMinY, 0, 0, dialogWidth, dialogHeight)
for (e in dialogChildren) {
e.render(matrixStack, mouseX, mouseY, delta)
}
} else {
drawMouseoverTooltip(matrixStack, mouseX, mouseY)
}
private fun showRequestAmountDialog(stack: ItemStack) {
val vc = TerminalRequestAmountViewController(this, stack)
addWindow(Window(vc))
amountVC = vc
}
@ExperimentalUnsignedTypes
@ -299,21 +129,29 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory,
matrixStack.pop()
}
private fun isPointInsScrollThumb(mouseX: Double, mouseY: Double): Boolean {
override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) {
super.drawBackground(matrixStack, delta, mouseX, mouseY)
RenderSystem.color4f(1f, 1f, 1f, 1f)
client!!.textureManager.bindTexture(BACKGROUND)
val x = (width - backgroundWidth) / 2
val y = (height - backgroundHeight) / 2
val thumbMinX = x + 232
val thumbMaxX = thumbMinX + thumbWidth
val thumbMinY = y + scrollThumbTop
val thumbMaxY = thumbMinY + thumbHeight
return mouseX >= thumbMinX && mouseX < thumbMaxX && mouseY >= thumbMinY && mouseY < thumbMaxY
drawTexture(matrixStack, x, y, 0, 0, backgroundWidth, backgroundHeight)
}
override fun tick() {
super.tick()
if (amountVC != null) {
amountVC!!.field.tick()
} else {
terminalVC.searchField.tick()
}
}
override fun onMouseClick(slot: Slot?, invSlot: Int, clickData: Int, type: SlotActionType?) {
super.onMouseClick(slot, invSlot, clickData, type)
updateFocusedElement()
if (slot != null && !slot.stack.isEmpty && handler.isNetworkSlot(slot.id)) {
val stack = slot.stack
@ -325,210 +163,22 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory,
// right click, request half stack
requestItem(stack, ceil(min(stack.count, stack.maxCount) / 2f).toInt())
} else {
dialogStack = stack
showingAmountDialog = true
searchBox.setSelected(false)
showRequestAmountDialog(stack)
// todo
// dialogStack = stack
// showingAmountDialog = true
// searchBox.setSelected(false)
}
}
}
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (showingAmountDialog) {
for (e in dialogChildren) {
if (e.mouseClicked(mouseX, mouseY, button)) {
return true
}
}
return false
private val fakeFocusedElement = TextFieldWidget(textRenderer, 0, 0, 0, 0, LiteralText(""))
override fun getFocused(): Element? {
return if (windows.last().firstResponder != null) {
fakeFocusedElement
} else {
if (isPointInsScrollThumb(mouseX, mouseY)) {
isDraggingScrollThumb = true
return true
}
return super.mouseClicked(mouseX, mouseY, button)
}
}
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
if (showingAmountDialog) {
return false
} else if (isDraggingScrollThumb) {
scrollPosition = (mouseY.toFloat() - (y + trackMinY) - 7.5f) / (trackHeight - 15)
scrollPosition = MathHelper.clamp(scrollPosition, 0f, 1f)
requestUpdatedItems()
return true
} else {
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
}
}
override fun mouseMoved(d: Double, e: Double) {
if (showingAmountDialog) {
} else {
super.mouseMoved(d, e)
}
}
override fun mouseReleased(d: Double, e: Double, i: Int): Boolean {
if (showingAmountDialog) {
return false
} else {
isDraggingScrollThumb = false
return super.mouseReleased(d, e, i)
}
}
override fun mouseScrolled(mouseX: Double, mouseY: Double, amount: Double): Boolean {
if (showingAmountDialog) {
return false
} else {
var newOffsetInRows = handler.currentScrollOffsetInRows() - amount.toInt()
newOffsetInRows = MathHelper.clamp(newOffsetInRows, 0, handler.maxScrollOffsetInRows())
val newScrollPosition = newOffsetInRows / handler.maxScrollOffsetInRows().toFloat()
scrollPosition = newScrollPosition
requestUpdatedItems()
return super.mouseScrolled(mouseX, mouseY, amount)
}
}
override fun charTyped(c: Char, i: Int): Boolean {
if (showingAmountDialog) {
return amountBox.charTyped(c, i)
} else {
val oldText = searchBox.text
if (searchBox.charTyped(c, i)) {
if (searchBox.text != oldText) {
scrollPosition = 0f
requestUpdatedItems()
}
return true
}
return super.charTyped(c, i)
}
}
override fun keyPressed(key: Int, j: Int, k: Int): Boolean {
if (showingAmountDialog) {
return when (key) {
GLFW.GLFW_KEY_ESCAPE -> {
showingAmountDialog = false
true
}
GLFW.GLFW_KEY_ENTER -> {
doDialogRequest()
true
}
else -> {
amountBox.keyPressed(key, j, k)
}
}
} else {
val oldText = searchBox.text
if (searchBox.keyPressed(key, j, k)) {
if (searchBox.text != oldText) {
scrollPosition = 0f
requestUpdatedItems()
}
return true
}
return if (searchBox.isFocused && searchBox.isVisible && key != GLFW.GLFW_KEY_ESCAPE) {
true
} else {
super.keyPressed(key, j, k)
}
}
}
private fun doDialogRequest() {
showingAmountDialog = false
requestItem(dialogStack, amountBox.intValue)
}
private fun requestItem(stack: ItemStack, amount: Int) {
val netHandler = MinecraftClient.getInstance().player!!.networkHandler
val packet = C2STerminalRequestItem(handler.terminal, stack, amount)
netHandler.sendPacket(packet)
}
private var TextFieldWidget.intValue: Int
get() = if (text.isEmpty()) 0 else Integer.parseInt(text)
set(value) {
text = value.toString()
setSelected(true)
}
class SmallButton(x: Int, y: Int, width: Int, title: Text, action: PressAction): ButtonWidget(x, y, width, 14, title, action) {
@ExperimentalUnsignedTypes
override fun renderButton(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
val client = MinecraftClient.getInstance()
client.textureManager.bindTexture(DIALOG)
RenderSystem.color4f(1f, 1f, 1f, 1f)
val v = if (isHovered) 142 else 128
RenderSystem.enableBlend()
RenderSystem.defaultBlendFunc()
RenderSystem.enableDepthTest()
drawTexture(matrixStack, x, y, 0, v, width / 2, height)
drawTexture(matrixStack, x + width / 2, y, 200 - width / 2, v, width / 2, height)
drawCenteredText(matrixStack, client.textRenderer, message, x + width / 2, y + (height - 8) / 2, 0xffffffffu.toInt())
}
}
class SortButton(
x: Int,
y: Int,
var mode: SortMode,
val onChange: (SortMode) -> Unit,
val doRenderTooltip: (MatrixStack, Text, Int, Int) -> Unit
): AbstractPressableButtonWidget(x, y, 20, 20, LiteralText("")) {
override fun onPress() {}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if ((button == 0 || button == 1) && clicked(mouseX, mouseY)) {
val newVal = if (button == 0) mode.next else mode.prev
mode = newVal
onChange(mode)
playDownSound(MinecraftClient.getInstance().soundManager)
return true
}
return false
}
override fun renderButton(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
val client = MinecraftClient.getInstance()
RenderSystem.color4f(1f, 1f, 1f, 1f)
RenderSystem.enableBlend()
RenderSystem.defaultBlendFunc()
RenderSystem.enableDepthTest()
client.textureManager.bindTexture(WIDGETS_LOCATION)
val k = getYImage(isHovered)
drawTexture(matrixStack, x, y, 0, 46 + k * 20, width / 2, height)
drawTexture(matrixStack, x + width / 2, y, 200 - width / 2, 46 + k * 20, width / 2, height)
client.textureManager.bindTexture(BACKGROUND)
val u: Int = when (mode) {
SortMode.COUNT_HIGH_FIRST -> 0
SortMode.COUNT_LOW_FIRST -> 16
SortMode.ALPHABETICAL -> 32
}
drawTexture(matrixStack, x + 2, y + 2, u, 230, 16, 16)
if (isHovered) {
renderToolTip(matrixStack, mouseX, mouseY)
}
}
override fun renderToolTip(matrixStack: MatrixStack, mouseX: Int, mouseY: Int) {
val text = LiteralText("")
text.append("Sort by: ")
text.append(mode.tooltip)
doRenderTooltip(matrixStack, text, mouseX, mouseY)
null
}
}

View File

@ -40,7 +40,7 @@ class TerminalScreenHandler(syncId: Int, val playerInv: PlayerInventory, val ter
private set
var totalEntries = 0
private set
private var scrollPosition = 0f
var scrollPosition = 0f
private var itemEntries = listOf<Entry>()
set(value) {
field = value

View File

@ -0,0 +1,159 @@
package net.shadowfacts.phycon.block.terminal
import net.minecraft.text.TranslatableText
import net.minecraft.util.math.MathHelper
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.view.Label
import net.shadowfacts.cacao.view.View
import net.shadowfacts.cacao.view.textfield.TextField
import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.util.SortMode
/**
* @author shadowfacts
*/
class TerminalViewController(
val screen: TerminalScreen,
val handler: TerminalScreenHandler,
val terminal: TerminalBlockEntity,
): ViewController() {
private lateinit var scrollTrack: ScrollTrackView
lateinit var sortMode: SortModeButton
private set
lateinit var searchField: TextField
private set
override fun loadView() {
view = ScrollHandlingView(this)
}
override fun viewDidLoad() {
super.viewDidLoad()
val pane = view.addLayoutGuide()
view.solver.dsl {
pane.centerXAnchor equalTo view.centerXAnchor
pane.centerYAnchor equalTo view.centerYAnchor
pane.widthAnchor equalTo screen.backgroundWidth
pane.heightAnchor equalTo screen.backgroundHeight
}
val buffer = view.addLayoutGuide()
view.solver.dsl {
buffer.leftAnchor equalTo (pane.leftAnchor + 7)
buffer.topAnchor equalTo (pane.topAnchor + 17)
buffer.widthAnchor equalTo (18 * 3)
buffer.heightAnchor equalTo (18 * 6)
}
val network = view.addLayoutGuide()
view.solver.dsl {
network.leftAnchor equalTo (pane.leftAnchor + 65)
network.topAnchor equalTo buffer.topAnchor
network.widthAnchor equalTo (18 * 9)
network.heightAnchor equalTo (18 * 6)
}
val playerInv = view.addLayoutGuide()
view.solver.dsl {
playerInv.leftAnchor equalTo network.leftAnchor
playerInv.topAnchor equalTo (pane.topAnchor + 139)
playerInv.widthAnchor equalTo (18 * 9)
playerInv.heightAnchor equalTo 76
}
val titleLabel = view.addSubview(Label(screen.title)).apply {
textColor = Color.TEXT
}
val playerInvLabel = view.addSubview(Label(handler.playerInv.displayName)).apply {
textColor = Color.TEXT
}
val bufferLabel = view.addSubview(Label(TranslatableText("gui.phycon.terminal_buffer"))).apply {
textColor = Color.TEXT
}
searchField = view.addSubview(TerminalSearchField()).apply {
handler = ::searchFieldChanged
drawBackground = false
}
searchField.becomeFirstResponder()
scrollTrack = view.addSubview(ScrollTrackView(::scrollPositionChanged))
sortMode = view.addSubview(SortModeButton(SortMode.COUNT_HIGH_FIRST)).apply {
handler = ::sortModeChanged
}
view.solver.dsl {
titleLabel.leftAnchor equalTo network.leftAnchor
titleLabel.topAnchor equalTo (pane.topAnchor + 6)
bufferLabel.leftAnchor equalTo buffer.leftAnchor
bufferLabel.topAnchor equalTo titleLabel.topAnchor
playerInvLabel.leftAnchor equalTo playerInv.leftAnchor
playerInvLabel.topAnchor equalTo (pane.bottomAnchor - 94)
searchField.leftAnchor equalTo (pane.leftAnchor + 138)
searchField.topAnchor equalTo (pane.topAnchor + 5)
searchField.widthAnchor equalTo 80
searchField.heightAnchor equalTo 9
scrollTrack.leftAnchor equalTo (pane.leftAnchor + 232)
scrollTrack.topAnchor equalTo (network.topAnchor + 1)
scrollTrack.bottomAnchor equalTo (network.bottomAnchor - 1)
scrollTrack.widthAnchor equalTo 12
sortMode.leftAnchor equalTo pane.rightAnchor + 4
sortMode.topAnchor equalTo pane.topAnchor
sortMode.widthAnchor equalTo 20
sortMode.heightAnchor equalTo 20
}
}
private fun searchFieldChanged(field: TextField) {
screen.searchQuery = field.text
screen.requestUpdatedItems()
}
private fun scrollPositionChanged(track: ScrollTrackView) {
val oldOffset = handler.currentScrollOffsetInRows()
handler.scrollPosition = track.scrollPosition.toFloat()
screen.scrollPosition = track.scrollPosition
if (handler.currentScrollOffsetInRows() != oldOffset) {
screen.requestUpdatedItems()
}
}
private fun sortModeChanged(button: SortModeButton) {
screen.sortMode = button.value
screen.requestUpdatedItems()
}
class TerminalSearchField: TextField("") {
override fun resignFirstResponder() {
// no-op
}
}
class ScrollHandlingView(val vc: TerminalViewController): View() {
override fun mouseScrolled(point: Point, amount: Double): Boolean {
var newOffsetInRows = vc.handler.currentScrollOffsetInRows() - amount.toInt()
newOffsetInRows = MathHelper.clamp(newOffsetInRows, 0, vc.handler.maxScrollOffsetInRows())
if (newOffsetInRows != vc.handler.currentScrollOffsetInRows()) {
val newScrollPosition = newOffsetInRows / vc.handler.maxScrollOffsetInRows().toDouble()
vc.screen.scrollPosition = newScrollPosition
vc.scrollTrack.scrollPosition = newScrollPosition
vc.screen.requestUpdatedItems()
}
return true
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 B

After

Width:  |  Height:  |  Size: 2.2 KiB