Compare commits

...

4 Commits

Author SHA1 Message Date
Shadowfacts 3e66d7439a
Convert Terminal screen to Cacao 2021-03-20 22:31:53 -04:00
Shadowfacts 2774cabfcc
Cacao: Misc things 2021-03-20 14:48:59 -04:00
Shadowfacts 81ce590231
Tweak Cable recipes 2021-03-20 12:15:47 -04:00
Shadowfacts f0fe1e4a3d
Cacao: Add layout guides 2021-03-20 11:40:00 -04:00
37 changed files with 782 additions and 501 deletions

View File

@ -19,8 +19,10 @@ object PhyConPlugin: REIPluginV0 {
override fun registerBounds(helper: DisplayHelper) { override fun registerBounds(helper: DisplayHelper) {
BaseBoundsHandler.getInstance().registerExclusionZones(TerminalScreen::class.java) { BaseBoundsHandler.getInstance().registerExclusionZones(TerminalScreen::class.java) {
val screen = MinecraftClient.getInstance().currentScreen as TerminalScreen val screen = MinecraftClient.getInstance().currentScreen as TerminalScreen
val button = screen.terminalVC.sortMode
val rect = button.convert(button.bounds, to = null)
listOf( 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 package net.shadowfacts.cacao
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.gui.screen.ingame.HandledScreen import net.minecraft.client.gui.screen.ingame.HandledScreen
import net.minecraft.client.util.math.MatrixStack import net.minecraft.client.util.math.MatrixStack
import net.minecraft.entity.player.PlayerInventory import net.minecraft.entity.player.PlayerInventory
@ -57,8 +58,17 @@ open class CacaoHandledScreen<Handler: ScreenHandler>(
} }
} }
override fun onClose() {
super.onClose()
windows.forEach {
// todo: VC callbacks
it.firstResponder = null
}
}
override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) { override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) {
renderBackground(matrixStack)
} }
override fun drawForeground(matrixStack: MatrixStack, mouseX: Int, mouseY: Int) { override fun drawForeground(matrixStack: MatrixStack, mouseX: Int, mouseY: Int) {
@ -67,15 +77,28 @@ open class CacaoHandledScreen<Handler: ScreenHandler>(
override fun render(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) { override fun render(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
val mouse = Point(mouseX, mouseY) 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 (it is ScreenHandlerWindow) {
if (index == windows.size - 1) { if (i == windows.size - 1) {
super.render(matrixStack, mouseX, mouseY, delta) super.render(matrixStack, mouseX, mouseY, delta)
} else { } else {
// if the screen handler window is not the frontmost, we fake the mouse x/y to disable the slot mouseover effect // 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) super.render(matrixStack, -1, -1, delta)
} }
RenderSystem.popMatrix()
} }
it.draw(matrixStack, mouse, delta) it.draw(matrixStack, mouse, delta)
} }
@ -95,6 +118,38 @@ open class CacaoHandledScreen<Handler: ScreenHandler>(
} }
} }
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
val window = windows.lastOrNull()
val startPoint = Point(mouseX, mouseY)
val delta = Point(deltaX, deltaY)
val result = window?.mouseDragged(startPoint, delta, MouseButton.fromMC(button))
return if (result == true) {
true
} else if (window is ScreenHandlerWindow) {
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
} else {
false
}
}
override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
val window = windows.lastOrNull()
val result = window?.mouseReleased(Point(mouseX, mouseY), MouseButton.fromMC(button))
return if (result == true) {
true
} else if (window is ScreenHandlerWindow) {
super.mouseReleased(mouseX, mouseY, button)
} else {
false
}
}
override fun mouseScrolled(mouseX: Double, mouseY: Double, amount: Double): Boolean {
val window = windows.lastOrNull()
val result = window?.mouseScrolled(Point(mouseX, mouseY), amount)
return result == true
}
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
val modifiersSet by lazy { KeyModifiers(modifiers) } val modifiersSet by lazy { KeyModifiers(modifiers) }
if (findResponder { it.keyPressed(keyCode, modifiersSet) }) { if (findResponder { it.keyPressed(keyCode, modifiersSet) }) {

View File

@ -119,6 +119,12 @@ open class CacaoScreen(title: Text = LiteralText("CacaoScreen")): Screen(title),
return result == true return result == true
} }
override fun mouseScrolled(mouseX: Double, mouseY: Double, amount: Double): Boolean {
val window = windows.lastOrNull()
val result = window?.mouseScrolled(Point(mouseX, mouseY), amount)
return result == true
}
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
val modifiersSet by lazy { KeyModifiers(modifiers) } val modifiersSet by lazy { KeyModifiers(modifiers) }
if (findResponder { it.keyPressed(keyCode, modifiersSet) }) { if (findResponder { it.keyPressed(keyCode, modifiersSet) }) {

View File

@ -1,5 +1,6 @@
package net.shadowfacts.cacao package net.shadowfacts.cacao
import net.shadowfacts.cacao.util.LayoutGuide
import net.shadowfacts.cacao.view.View import net.shadowfacts.cacao.view.View
import no.birkett.kiwi.Variable import no.birkett.kiwi.Variable
@ -9,9 +10,22 @@ import no.birkett.kiwi.Variable
* *
* @author shadowfacts * @author shadowfacts
*/ */
class LayoutVariable(val owner: View, val property: String): Variable("LayoutVariable") { class LayoutVariable(
val view: View?,
val layoutGuide: LayoutGuide?,
val property: String,
): Variable("LayoutVariable") {
override fun getName() = "$owner.$property" constructor(view: View, property: String): this(view, null, property)
constructor(layoutGuide: LayoutGuide, property: String): this(null, layoutGuide, property)
init {
if ((view == null) == (layoutGuide == null)) {
throw RuntimeException("LayoutVariable must be constructed with either a view or layout guide")
}
}
override fun getName() = "${view ?: layoutGuide}.$property"
override fun toString() = "LayoutVariable(name=$name, value=$value)" override fun toString() = "LayoutVariable(name=$name, value=$value)"

View File

@ -0,0 +1,33 @@
package net.shadowfacts.cacao.util
import net.shadowfacts.cacao.LayoutVariable
import net.shadowfacts.cacao.geometry.Rect
import net.shadowfacts.cacao.view.View
/**
* A layout guide is a non-view object that represents a rectangular area for the purposes of constraint-based layout.
* It allows you to define complex layouts without needing empty container views.
*
* A layout guide is always owned by a view. The owning view's dimensions are not necessarily tied to the layout guide's.
*
* To create a layout guide, call [View.addLayoutGuide] on the owning view.
*
* @author shadowfacts
*/
class LayoutGuide(
val owningView: View,
) {
val leftAnchor: LayoutVariable = LayoutVariable(this, "left")
val rightAnchor: LayoutVariable = LayoutVariable(this, "right")
val topAnchor: LayoutVariable = LayoutVariable(this, "top")
val bottomAnchor: LayoutVariable = LayoutVariable(this, "bottom")
val widthAnchor: LayoutVariable = LayoutVariable(this, "width")
val heightAnchor: LayoutVariable = LayoutVariable(this, "height")
val centerXAnchor: LayoutVariable = LayoutVariable(this, "centerX")
val centerYAnchor: LayoutVariable = LayoutVariable(this, "centerY")
val frame: Rect
get() = Rect(leftAnchor.value - owningView.leftAnchor.value, topAnchor.value - owningView.topAnchor.value, widthAnchor.value, heightAnchor.value)
}

View File

@ -6,6 +6,7 @@ import net.shadowfacts.cacao.geometry.Rect
import net.shadowfacts.cacao.util.texture.NinePatchTexture import net.shadowfacts.cacao.util.texture.NinePatchTexture
import net.shadowfacts.cacao.util.RenderHelper import net.shadowfacts.cacao.util.RenderHelper
import net.shadowfacts.cacao.util.properties.ResettableLazyProperty import net.shadowfacts.cacao.util.properties.ResettableLazyProperty
import kotlin.math.roundToInt
/** /**
* A helper class for drawing a [NinePatchTexture] in a view. * 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) { private fun drawEdges(matrixStack: MatrixStack) {
// Horizontal // 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, 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) 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) { 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, 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) 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 // 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, 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) 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) { 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, 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) 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) { 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()) 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) { if (remHeight > 0) {
drawCenterRow(matrixStack, center.bottom - remHeight, remHeight.toDouble()) drawCenterRow(matrixStack, center.bottom - remHeight, remHeight.toDouble())
} }
} }
private fun drawCenterRow(matrixStack: MatrixStack, y: Double, height: Double) { 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) 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) { 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) RenderHelper.draw(matrixStack, center.right - remWidth, y, ninePatch.center.u, ninePatch.center.v, remWidth.toDouble(), height, ninePatch.texture.width, ninePatch.texture.height)
} }

View File

@ -13,6 +13,7 @@ import no.birkett.kiwi.Solver
import java.lang.RuntimeException import java.lang.RuntimeException
import java.util.* import java.util.*
import kotlin.collections.HashSet import kotlin.collections.HashSet
import kotlin.math.floor
/** /**
* The base Cacao View class. Provides layout anchors, properties, and helper methods. * The base Cacao View class. Provides layout anchors, properties, and helper methods.
@ -84,6 +85,17 @@ open class View(): Responder {
*/ */
val centerYAnchor = LayoutVariable(this, "centerY") val centerYAnchor = LayoutVariable(this, "centerY")
private val _layoutGuides = LinkedList<LayoutGuide>()
/**
* All the layout guides attached to this view.
*
* To add a layout guide, call [addLayoutGuide].
*
* @see LayoutGuide
*/
val layoutGuides: List<LayoutGuide> = _layoutGuides
/** /**
* Whether this view uses constraint-based layout. * Whether this view uses constraint-based layout.
* If `false`, the view's `frame` must be set manually and the layout anchors may not be used. * If `false`, the view's `frame` must be set manually and the layout anchors may not be used.
@ -229,7 +241,7 @@ open class View(): Responder {
for (b in a + 1 until variables.size) { for (b in a + 1 until variables.size) {
// if the variable views have no common ancestor after the removed view's superview is unset, // if the variable views have no common ancestor after the removed view's superview is unset,
// the constraint crossed the this<->view boundary and should be removed // the constraint crossed the this<->view boundary and should be removed
val ancestor = LowestCommonAncestor.find(variables[a].owner, variables[b].owner, View::superview) val ancestor = LowestCommonAncestor.find(variables[a].viewOrLayoutGuideView, variables[b].viewOrLayoutGuideView, View::superview)
if (ancestor == null) { if (ancestor == null) {
return@filter true return@filter true
} }
@ -247,6 +259,18 @@ open class View(): Responder {
// view.wasRemoved() // view.wasRemoved()
} }
/**
* Creates and returns a new layout guide with this view as its owner.
*/
fun addLayoutGuide(): LayoutGuide {
val guide = LayoutGuide(this)
_layoutGuides.add(guide)
if (hasSolver) {
guide.attachTo(solver)
}
return guide
}
/** /**
* Removes this view from its superview, if it has one. * Removes this view from its superview, if it has one.
*/ */
@ -283,6 +307,10 @@ open class View(): Responder {
open fun wasAdded() { open fun wasAdded() {
createInternalConstraints() createInternalConstraints()
updateIntrinsicContentSizeConstraints(null, intrinsicContentSize) updateIntrinsicContentSizeConstraints(null, intrinsicContentSize)
layoutGuides.forEach {
it.attachTo(solver)
}
} }
/** /**
@ -341,7 +369,7 @@ open class View(): Responder {
*/ */
open fun draw(matrixStack: MatrixStack, mouse: Point, delta: Float) { open fun draw(matrixStack: MatrixStack, mouse: Point, delta: Float) {
RenderHelper.pushMatrix() RenderHelper.pushMatrix()
RenderHelper.translate(frame.left, frame.top) RenderHelper.translate(floor(frame.left), floor(frame.top))
RenderHelper.fill(matrixStack, bounds, backgroundColor) RenderHelper.fill(matrixStack, bounds, backgroundColor)
@ -419,6 +447,23 @@ open class View(): Responder {
return false return false
} }
open fun mouseDragEnded(point: Point, mouseButton: MouseButton) {
val view = subviewsAtPoint(point).maxByOrNull(View::zIndex)
if (view != null) {
val pointInView = convert(point, to = view)
return view.mouseDragEnded(pointInView, mouseButton)
}
}
open fun mouseScrolled(point: Point, amount: Double): Boolean {
val view = subviewsAtPoint(point).maxByOrNull(View::zIndex)
if (view != null) {
val pointInView = convert(point, to = view)
return view.mouseScrolled(pointInView, amount)
}
return false
}
/** /**
* Converts the given point in this view's coordinate system to the coordinate system of another view or the window. * Converts the given point in this view's coordinate system to the coordinate system of another view or the window.
* *
@ -463,3 +508,15 @@ open class View(): Responder {
} }
} }
private fun LayoutGuide.attachTo(solver: Solver) {
solver.dsl {
rightAnchor equalTo (leftAnchor + widthAnchor)
bottomAnchor equalTo (topAnchor + heightAnchor)
centerXAnchor equalTo (leftAnchor + widthAnchor / 2)
centerYAnchor equalTo (topAnchor + heightAnchor / 2)
}
}
private val LayoutVariable.viewOrLayoutGuideView: View
get() = view ?: layoutGuide!!.owningView

View File

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

View File

@ -1,6 +1,7 @@
package net.shadowfacts.cacao.view.textfield package net.shadowfacts.cacao.view.textfield
import net.minecraft.client.MinecraftClient import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.screen.TickableElement
import net.minecraft.client.gui.widget.TextFieldWidget import net.minecraft.client.gui.widget.TextFieldWidget
import net.minecraft.client.util.math.MatrixStack import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.LiteralText import net.minecraft.text.LiteralText
@ -11,6 +12,7 @@ import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.RenderHelper import net.shadowfacts.cacao.util.RenderHelper
import net.shadowfacts.cacao.view.View import net.shadowfacts.cacao.view.View
import net.shadowfacts.phycon.mixin.client.TextFieldWidgetAccessor import net.shadowfacts.phycon.mixin.client.TextFieldWidgetAccessor
import org.lwjgl.glfw.GLFW
/** /**
* An abstract text field class. Cannot be constructed directly, use for creating other text fields with more specific * An abstract text field class. Cannot be constructed directly, use for creating other text fields with more specific
@ -23,7 +25,7 @@ import net.shadowfacts.phycon.mixin.client.TextFieldWidgetAccessor
*/ */
abstract class AbstractTextField<Impl: AbstractTextField<Impl>>( abstract class AbstractTextField<Impl: AbstractTextField<Impl>>(
initialText: String initialText: String
): View() { ): View(), TickableElement {
/** /**
* A function that is invoked when the text in this text field changes. * A function that is invoked when the text in this text field changes.
@ -159,7 +161,11 @@ abstract class AbstractTextField<Impl: AbstractTextField<Impl>>(
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
handler?.invoke(this as Impl) handler?.invoke(this as Impl)
} }
return result return result || (isFirstResponder && keyCode != GLFW.GLFW_KEY_ESCAPE)
}
override fun tick() {
minecraftWidget.tick()
} }
// todo: label for the TextFieldWidget? // todo: label for the TextFieldWidget?

View File

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

View File

@ -234,7 +234,8 @@ open class Window(
fun mouseDragged(startPoint: Point, delta: Point, mouseButton: MouseButton): Boolean { fun mouseDragged(startPoint: Point, delta: Point, mouseButton: MouseButton): Boolean {
val currentlyDraggedView = this.currentDragReceiver val currentlyDraggedView = this.currentDragReceiver
if (currentlyDraggedView != null) { if (currentlyDraggedView != null) {
return currentlyDraggedView.mouseDragged(startPoint, delta, mouseButton) val pointInView = viewController.view.convert(startPoint, to = currentlyDraggedView)
return currentlyDraggedView.mouseDragged(pointInView, delta, mouseButton)
} else if (startPoint in viewController.view.frame) { } else if (startPoint in viewController.view.frame) {
val startInView = val startInView =
Point(startPoint.x - viewController.view.frame.left, startPoint.y - viewController.view.frame.top) Point(startPoint.x - viewController.view.frame.left, startPoint.y - viewController.view.frame.top)
@ -246,7 +247,12 @@ open class Window(
view = view.subviewsAtPoint(pointInView).maxByOrNull(View::zIndex) view = view.subviewsAtPoint(pointInView).maxByOrNull(View::zIndex)
} }
this.currentDragReceiver = view ?: prevView this.currentDragReceiver = view ?: prevView
return this.currentDragReceiver?.mouseDragged(startPoint, delta, mouseButton) ?: false return if (this.currentDragReceiver != null) {
val pointInView = viewController.view.convert(startPoint, to = this.currentDragReceiver!!)
this.currentDragReceiver!!.mouseDragged(pointInView, delta, mouseButton)
} else {
false
}
} }
return false return false
} }
@ -254,10 +260,16 @@ open class Window(
fun mouseReleased(point: Point, mouseButton: MouseButton): Boolean { fun mouseReleased(point: Point, mouseButton: MouseButton): Boolean {
val currentlyDraggedView = this.currentDragReceiver val currentlyDraggedView = this.currentDragReceiver
if (currentlyDraggedView != null) { if (currentlyDraggedView != null) {
val pointInView = viewController.view.convert(point, to = currentlyDraggedView)
currentlyDraggedView.mouseDragEnded(pointInView, mouseButton)
this.currentDragReceiver = null this.currentDragReceiver = null
return true return true
} }
return false return false
} }
fun mouseScrolled(point: Point, amount: Double): Boolean {
return viewController.view.mouseScrolled(point, amount)
}
} }

View File

@ -129,10 +129,9 @@ class RedstoneEmitterScreen(
inv.leftAnchor equalTo (minX + 8) inv.leftAnchor equalTo (minX + 8)
inv.topAnchor equalTo (minY + 72) inv.topAnchor equalTo (minY + 72)
// offset by 1 on each edge to account for MC border
field.widthAnchor equalTo 82 field.widthAnchor equalTo 82
field.heightAnchor equalTo 11 field.heightAnchor equalTo 11
field.leftAnchor equalTo (minX + 56) field.leftAnchor equalTo (minX + 57)
field.topAnchor equalTo (minY + 23) field.topAnchor equalTo (minY + 23)
hStack.centerXAnchor equalTo view.centerXAnchor 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 com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.MinecraftClient import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.DrawableHelper import net.minecraft.client.gui.DrawableHelper
import net.minecraft.client.gui.screen.ingame.HandledScreen import net.minecraft.client.gui.Element
import net.minecraft.client.gui.widget.AbstractButtonWidget import net.minecraft.client.gui.screen.TickableElement
import net.minecraft.client.gui.widget.AbstractPressableButtonWidget
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.gui.widget.TextFieldWidget import net.minecraft.client.gui.widget.TextFieldWidget
import net.minecraft.client.render.Tessellator import net.minecraft.client.render.Tessellator
import net.minecraft.client.render.VertexConsumerProvider 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.screen.slot.SlotActionType
import net.minecraft.text.LiteralText import net.minecraft.text.LiteralText
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.text.TranslatableText
import net.minecraft.util.Identifier 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.PhysicalConnectivity
import net.shadowfacts.phycon.networking.C2STerminalRequestItem import net.shadowfacts.phycon.networking.C2STerminalRequestItem
import net.shadowfacts.phycon.networking.C2STerminalUpdateDisplayedItems import net.shadowfacts.phycon.networking.C2STerminalUpdateDisplayedItems
import net.shadowfacts.phycon.util.SortMode 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.math.RoundingMode
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.LinkedList
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.floor
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
// todo: translate title class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, title: Text): CacaoHandledScreen<TerminalScreenHandler>(handler, playerInv, title) {
class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, title: Text): HandledScreen<TerminalScreenHandler>(handler, playerInv, title) {
companion object { companion object {
private val BACKGROUND = Identifier(PhysicalConnectivity.MODID, "textures/gui/terminal.png") 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 val backgroundWidth: Int
private lateinit var sortButton: SortButton get() = backgroundWidth
var sortButtonX: Int = 0 val backgroundHeight: Int
private set get() = backgroundHeight
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>()
private var scrollPosition = 0f // val tickableElements = LinkedList<TickableElement>()
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()
private val dialogWidth = 158 val terminalVC = TerminalViewController(this, handler, handler.terminal)
private val dialogHeight = 62 var amountVC: TerminalRequestAmountViewController? = null
var searchQuery = ""
var scrollPosition = 0.0
var sortMode = SortMode.COUNT_HIGH_FIRST
init { init {
backgroundWidth = 252 backgroundWidth = 252
backgroundHeight = 222 backgroundHeight = 222
}
override fun init() { addWindow(ScreenHandlerWindow(handler, terminalVC))
super.init()
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() requestUpdatedItems()
} }
private fun updateFocusedElement() { fun requestItem(stack: ItemStack, amount: Int) {
focused = if (showingAmountDialog) { val netHandler = MinecraftClient.getInstance().player!!.networkHandler
amountBox val packet = C2STerminalRequestItem(handler.terminal, stack, amount)
} else if (searchBox.isFocused) { netHandler.sendPacket(packet)
searchBox
} else {
null
}
} }
private fun requestUpdatedItems() { fun requestUpdatedItems() {
val player = MinecraftClient.getInstance().player!! 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() { private fun showRequestAmountDialog(stack: ItemStack) {
super.tick() val vc = TerminalRequestAmountViewController(this, stack)
searchBox.tick() addWindow(Window(vc))
amountBox.tick() amountVC = vc
}
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)
}
} }
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@ -299,21 +129,29 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory,
matrixStack.pop() 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 x = (width - backgroundWidth) / 2
val y = (height - backgroundHeight) / 2 val y = (height - backgroundHeight) / 2
val thumbMinX = x + 232 drawTexture(matrixStack, x, y, 0, 0, backgroundWidth, backgroundHeight)
val thumbMaxX = thumbMinX + thumbWidth }
val thumbMinY = y + scrollThumbTop
val thumbMaxY = thumbMinY + thumbHeight override fun tick() {
return mouseX >= thumbMinX && mouseX < thumbMaxX && mouseY >= thumbMinY && mouseY < thumbMaxY super.tick()
if (amountVC != null) {
amountVC!!.field.tick()
} else {
terminalVC.searchField.tick()
}
} }
override fun onMouseClick(slot: Slot?, invSlot: Int, clickData: Int, type: SlotActionType?) { override fun onMouseClick(slot: Slot?, invSlot: Int, clickData: Int, type: SlotActionType?) {
super.onMouseClick(slot, invSlot, clickData, type) super.onMouseClick(slot, invSlot, clickData, type)
updateFocusedElement()
if (slot != null && !slot.stack.isEmpty && handler.isNetworkSlot(slot.id)) { if (slot != null && !slot.stack.isEmpty && handler.isNetworkSlot(slot.id)) {
val stack = slot.stack val stack = slot.stack
@ -325,210 +163,22 @@ class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory,
// right click, request half stack // right click, request half stack
requestItem(stack, ceil(min(stack.count, stack.maxCount) / 2f).toInt()) requestItem(stack, ceil(min(stack.count, stack.maxCount) / 2f).toInt())
} else { } else {
dialogStack = stack showRequestAmountDialog(stack)
showingAmountDialog = true // todo
searchBox.setSelected(false) // dialogStack = stack
// showingAmountDialog = true
// searchBox.setSelected(false)
} }
} }
} }
} }
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { private val fakeFocusedElement = TextFieldWidget(textRenderer, 0, 0, 0, 0, LiteralText(""))
if (showingAmountDialog) { override fun getFocused(): Element? {
for (e in dialogChildren) { return if (windows.last().firstResponder != null) {
if (e.mouseClicked(mouseX, mouseY, button)) { fakeFocusedElement
return true
}
}
return false
} else { } else {
if (isPointInsScrollThumb(mouseX, mouseY)) { null
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)
} }
} }

View File

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

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:black_carpet"}, "W": {"item": "minecraft:black_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_black", "item": "phycon:cable_black",
"count": 3 "count": 6
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:blue_carpet"}, "W": {"item": "minecraft:blue_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_blue", "item": "phycon:cable_blue",
"count": 3 "count": 6
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:brown_carpet"}, "W": {"item": "minecraft:brown_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_brown", "item": "phycon:cable_brown",
"count": 3 "count": 6
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:cyan_carpet"}, "W": {"item": "minecraft:cyan_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_cyan", "item": "phycon:cable_cyan",
"count": 3 "count": 6
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:gray_carpet"}, "W": {"item": "minecraft:gray_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_gray", "item": "phycon:cable_gray",
"count": 3 "count": 6
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:green_carpet"}, "W": {"item": "minecraft:green_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_green", "item": "phycon:cable_green",
"count": 3 "count": 6
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:light_blue_carpet"}, "W": {"item": "minecraft:light_blue_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_light_blue", "item": "phycon:cable_light_blue",
"count": 3 "count": 6
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:light_gray_carpet"}, "W": {"item": "minecraft:light_gray_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_light_gray", "item": "phycon:cable_light_gray",
"count": 3 "count": 6
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:lime_carpet"}, "W": {"item": "minecraft:lime_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_lime", "item": "phycon:cable_lime",
"count": 3 "count": 6
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:orange_carpet"}, "W": {"item": "minecraft:orange_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_orange", "item": "phycon:cable_orange",
"count": 3 "count": 6
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:orange_carpet"}, "W": {"item": "minecraft:orange_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_orange", "item": "phycon:cable_orange",
"count": 3 "count": 6
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:pink_carpet"}, "W": {"item": "minecraft:pink_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_pink", "item": "phycon:cable_pink",
"count": 3 "count": 6
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:purple_carpet"}, "W": {"item": "minecraft:purple_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_purple", "item": "phycon:cable_purple",
"count": 3 "count": 6
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:red_carpet"}, "W": {"item": "minecraft:red_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_red", "item": "phycon:cable_red",
"count": 3 "count": 6
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:white_carpet"}, "W": {"item": "minecraft:white_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_white", "item": "phycon:cable_white",
"count": 3 "count": 6
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"CCC", "WWW",
"TRT", "TRT",
"CCC" "WWW"
], ],
"key": { "key": {
"C": {"item": "minecraft:yellow_carpet"}, "W": {"item": "minecraft:yellow_wool"},
"T": {"item": "phycon:twisted_pair"}, "T": {"item": "phycon:twisted_pair"},
"R": {"item": "minecraft:redstone"} "R": {"item": "minecraft:redstone"}
}, },
"result": { "result": {
"item": "phycon:cable_yellow", "item": "phycon:cable_yellow",
"count": 3 "count": 6
} }
} }

View File

@ -1,14 +1,15 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"NNN", "III",
" ", " ",
"NNN" "III"
], ],
"key": { "key": {
"N": {"tag": "c:copper_nuggets"} "I": {"tag": "c:copper_ingots"}
}, },
"result": { "result": {
"item": "phycon:twisted_pair" "item": "phycon:twisted_pair",
"count": 4
} }
} }