diff --git a/plugin/rei/src/main/kotlin/net/shadowfacts/phycon/plugin/rei/PhyConPlugin.kt b/plugin/rei/src/main/kotlin/net/shadowfacts/phycon/plugin/rei/PhyConPlugin.kt index 7ac874f..41b502d 100644 --- a/plugin/rei/src/main/kotlin/net/shadowfacts/phycon/plugin/rei/PhyConPlugin.kt +++ b/plugin/rei/src/main/kotlin/net/shadowfacts/phycon/plugin/rei/PhyConPlugin.kt @@ -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) ) } } diff --git a/src/main/kotlin/net/shadowfacts/cacao/CacaoHandledScreen.kt b/src/main/kotlin/net/shadowfacts/cacao/CacaoHandledScreen.kt index 70a7b1c..ef6ec91 100644 --- a/src/main/kotlin/net/shadowfacts/cacao/CacaoHandledScreen.kt +++ b/src/main/kotlin/net/shadowfacts/cacao/CacaoHandledScreen.kt @@ -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( } 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( 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) } diff --git a/src/main/kotlin/net/shadowfacts/cacao/view/NinePatchView.kt b/src/main/kotlin/net/shadowfacts/cacao/view/NinePatchView.kt index c188b64..39ee49f 100644 --- a/src/main/kotlin/net/shadowfacts/cacao/view/NinePatchView.kt +++ b/src/main/kotlin/net/shadowfacts/cacao/view/NinePatchView.kt @@ -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) } diff --git a/src/main/kotlin/net/shadowfacts/cacao/view/button/EnumButton.kt b/src/main/kotlin/net/shadowfacts/cacao/view/button/EnumButton.kt index 907ffab..f464bb7 100644 --- a/src/main/kotlin/net/shadowfacts/cacao/view/button/EnumButton.kt +++ b/src/main/kotlin/net/shadowfacts/cacao/view/button/EnumButton.kt @@ -41,7 +41,9 @@ class EnumButton>( value = when (mouseButton) { MouseButton.LEFT -> EnumHelper.next(value) MouseButton.RIGHT -> EnumHelper.previous(value) - else -> value + else -> { + return false + } } } diff --git a/src/main/kotlin/net/shadowfacts/cacao/view/textfield/AbstractTextField.kt b/src/main/kotlin/net/shadowfacts/cacao/view/textfield/AbstractTextField.kt index c3aeed3..fedf625 100644 --- a/src/main/kotlin/net/shadowfacts/cacao/view/textfield/AbstractTextField.kt +++ b/src/main/kotlin/net/shadowfacts/cacao/view/textfield/AbstractTextField.kt @@ -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>( 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>( 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 diff --git a/src/main/kotlin/net/shadowfacts/cacao/view/textfield/NumberField.kt b/src/main/kotlin/net/shadowfacts/cacao/view/textfield/NumberField.kt index 29b8f29..3cd99ea 100644 --- a/src/main/kotlin/net/shadowfacts/cacao/view/textfield/NumberField.kt +++ b/src/main/kotlin/net/shadowfacts/cacao/view/textfield/NumberField.kt @@ -3,7 +3,7 @@ package net.shadowfacts.cacao.view.textfield /** * @author shadowfacts */ -class NumberField( +open class NumberField( initialValue: Int, handler: ((NumberField) -> Unit)? = null, ): AbstractTextField(initialValue.toString()) { diff --git a/src/main/kotlin/net/shadowfacts/cacao/view/textfield/TextField.kt b/src/main/kotlin/net/shadowfacts/cacao/view/textfield/TextField.kt index 3c7965e..236da89 100644 --- a/src/main/kotlin/net/shadowfacts/cacao/view/textfield/TextField.kt +++ b/src/main/kotlin/net/shadowfacts/cacao/view/textfield/TextField.kt @@ -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(initialText) { diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/redstone_emitter/RedstoneEmitterScreen.kt b/src/main/kotlin/net/shadowfacts/phycon/block/redstone_emitter/RedstoneEmitterScreen.kt index 6c186a1..4388740 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/redstone_emitter/RedstoneEmitterScreen.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/redstone_emitter/RedstoneEmitterScreen.kt @@ -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 diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/ScrollTrackView.kt b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/ScrollTrackView.kt new file mode 100644 index 0000000..306e7cd --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/ScrollTrackView.kt @@ -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 + } + +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/SortModeButton.kt b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/SortModeButton.kt new file mode 100644 index 0000000..8ba1847 --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/SortModeButton.kt @@ -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( + 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) + } + +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalRequestAmountViewController.kt b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalRequestAmountViewController.kt new file mode 100644 index 0000000..3727c3a --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalRequestAmountViewController.kt @@ -0,0 +1,151 @@ +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() + screen.amountVC = null + } + + class AmountField(val vc: TerminalRequestAmountViewController): NumberField(1) { + override fun keyPressed(keyCode: Int, modifiers: KeyModifiers): Boolean { + return if (keyCode == GLFW.GLFW_KEY_ENTER) { + vc.doRequest() + true + } else { + super.keyPressed(keyCode, modifiers) + } + } + } + +} diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalScreen.kt b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalScreen.kt index b361340..dcc6a0e 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalScreen.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalScreen.kt @@ -3,10 +3,7 @@ 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.widget.TextFieldWidget import net.minecraft.client.render.Tessellator import net.minecraft.client.render.VertexConsumerProvider @@ -17,235 +14,64 @@ 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 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(handler, playerInv, title) { +class TerminalScreen(handler: TerminalScreenHandler, playerInv: PlayerInventory, title: Text): CacaoHandledScreen(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() + 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 terminalVC = TerminalViewController(this, handler, handler.terminal) + var amountVC: TerminalRequestAmountViewController? = null - private val dialogWidth = 158 - private val dialogHeight = 62 + 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 +125,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 +159,18 @@ 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) } } } } - 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 } } diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalScreenHandler.kt b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalScreenHandler.kt index 49e47dc..f6bd6fa 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalScreenHandler.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalScreenHandler.kt @@ -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() set(value) { field = value diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalViewController.kt b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalViewController.kt new file mode 100644 index 0000000..831c0f7 --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/TerminalViewController.kt @@ -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 + } + } + +} diff --git a/src/main/resources/assets/phycon/textures/gui/icons.png b/src/main/resources/assets/phycon/textures/gui/icons.png index 2e6b6a3..7e7d627 100644 Binary files a/src/main/resources/assets/phycon/textures/gui/icons.png and b/src/main/resources/assets/phycon/textures/gui/icons.png differ