Ah yes, gui bound rectangles

Fixes #73
This commit is contained in:
DBotThePony 2022-09-10 18:10:33 +07:00
parent 9cf7a4cdd9
commit f8e38949b5
Signed by: DBot
GPG Key ID: DCC23B5715498507
11 changed files with 171 additions and 46 deletions

View File

@ -423,19 +423,19 @@ private enum class PreviewScrollers(
LEFT_TO_RIGHT(
init = { it, random ->
it.xOffset = it.width
it.yOffset = random.nextFloat(-it.boundingHeight + 18f, 0f)
it.yOffset = random.nextFloat(-it.childrenRectHeight + 18f, 0f)
},
scroll = { it, _ ->
it.xOffset -= 1f
it.xOffset >= -it.boundingWidth
it.xOffset >= -it.childrenRectWidth
}
),
RIGHT_TO_LEFT(
init = { it, random ->
it.xOffset = -it.boundingWidth
it.yOffset = random.nextFloat(-it.boundingHeight + 18f, 0f)
it.xOffset = -it.childrenRectWidth
it.yOffset = random.nextFloat(-it.childrenRectHeight + 18f, 0f)
},
scroll = { it, _ ->
@ -446,8 +446,8 @@ private enum class PreviewScrollers(
BOTTOM_TO_TOP(
init = { it, random ->
it.yOffset = -it.boundingHeight
it.xOffset = random.nextFloat(-it.boundingWidth + 18f, 0f)
it.yOffset = -it.childrenRectHeight
it.xOffset = random.nextFloat(-it.childrenRectWidth + 18f, 0f)
},
scroll = { it, _ ->
@ -459,12 +459,12 @@ private enum class PreviewScrollers(
TOP_TO_BOTTOM(
init = { it, random ->
it.yOffset = it.height
it.xOffset = random.nextFloat(-it.boundingWidth + 18f, 0f)
it.xOffset = random.nextFloat(-it.childrenRectWidth + 18f, 0f)
},
scroll = { it, _ ->
it.yOffset -= 1f
it.yOffset >= it.boundingHeight
it.yOffset >= it.childrenRectHeight
}
)
}

View File

@ -55,7 +55,7 @@ class DriveViewerScreen(menu: DriveViewerMenu, inventory: Inventory, title: Comp
for (i in 0 until GRID_WIDTH * GRID_HEIGHT) {
object : AbstractSlotPanel<DriveViewerScreen>(this@DriveViewerScreen, grid, 0f, 0f) {
override fun getItemStack(): ItemStack {
override val itemStack: ItemStack get() {
val index = i + scrollBar.scroll * GRID_WIDTH
return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.item ?: ItemStack.EMPTY
}
@ -72,7 +72,7 @@ class DriveViewerScreen(menu: DriveViewerMenu, inventory: Inventory, title: Comp
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
renderSlotBackground(stack, mouseX, mouseY, partialTick)
renderRegular(stack, getItemStack(), "")
renderRegular(stack, itemStack, "")
}
override fun getItemStackTooltip(stack: ItemStack): List<Component> {

View File

@ -56,7 +56,7 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
object : AbstractSlotPanel<ItemMonitorScreen>(this@ItemMonitorScreen, gridPanel) {
private val index get() = i + viewScrollBar.scroll * ITEM_GRID_WIDTH
override fun getItemStack(): ItemStack {
override val itemStack: ItemStack get() {
return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.item ?: ItemStack.EMPTY
}
@ -179,7 +179,7 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
for (i in 0 until 9) {
object : AbstractSlotPanel<ItemMonitorScreen>(this@ItemMonitorScreen, craftingHistory) {
override fun getItemStack(): ItemStack {
override val itemStack: ItemStack get() {
return ItemStack(Items.ARROW, 42)
}

View File

@ -75,7 +75,7 @@ class MatterPanelScreen(
private val index: Int get() = (scrollBar.scroll + row) * GRID_WIDTH + i
override fun getItemStack(): ItemStack {
override val itemStack: ItemStack get() {
if (isPatternView) {
return menu.patterns.getOrNull(index)?.stack() ?: ItemStack.EMPTY
} else {
@ -132,7 +132,7 @@ class MatterPanelScreen(
dock = Dock.LEFT
}
override fun getItemStack(): ItemStack {
override val itemStack: ItemStack get() {
return menu.tasks.firstOrNull { it.id == task.id }?.let { it.stack((it.required + it.inProgress).coerceAtLeast(1)) } ?: task.stack((task.required + task.inProgress).coerceAtLeast(1))
}
@ -187,7 +187,7 @@ class MatterPanelScreen(
dockRight = 2f
}
override fun getItemStack(): ItemStack {
override val itemStack: ItemStack get() {
return pattern.stack()
}

View File

@ -20,6 +20,7 @@ import ru.dbotthepony.mc.otm.client.moveMousePosScaled
import ru.dbotthepony.mc.otm.client.screen.panels.*
import ru.dbotthepony.mc.otm.core.maxScrollDivision
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import java.util.Collections
/**
* This class encapsulate most of logic for handling EditablePanel and it's children.
@ -34,6 +35,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
constructor(menu: T, title: Component) : this(menu, menu.inventory, title)
protected val panels = ArrayDeque<EditablePanel<*>>()
val panelsView: List<EditablePanel<*>> = Collections.unmodifiableList(panels)
var inventoryFrame: FramePanel<MatteryScreen<*>>? = null
var mainFrame: FramePanel<MatteryScreen<*>>? = null

View File

@ -20,7 +20,7 @@ abstract class AbstractSlotPanel<out S : MatteryScreen<*>> @JvmOverloads constru
width: Float = SIZE,
height: Float = SIZE,
open val noItemIcon: SkinElement? = null
) : EditablePanel<S>(screen, parent, x, y, width, height) {
) : EditablePanel<S>(screen, parent, x, y, width, height), IItemStackPanel {
protected open fun renderSlotBackground(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
SLOT_BACKGROUND.render(stack, width = width, height = height)
}
@ -68,15 +68,13 @@ abstract class AbstractSlotPanel<out S : MatteryScreen<*>> @JvmOverloads constru
}
}
protected abstract fun getItemStack(): ItemStack
protected open fun getItemStackTooltip(stack: ItemStack): List<Component> {
return screen.getTooltipFromItem(stack)
}
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
renderSlotBackground(stack, mouseX, mouseY, partialTick)
val itemStack = getItemStack()
val itemStack = itemStack
renderRegular(stack, itemStack)
if (itemStack.isEmpty) {
@ -86,7 +84,7 @@ abstract class AbstractSlotPanel<out S : MatteryScreen<*>> @JvmOverloads constru
override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (isHovered) {
val itemstack = getItemStack()
val itemstack = itemStack
if (!itemstack.isEmpty) {
// val font = RenderProperties.get(itemstack).getFont(itemstack)

View File

@ -6,19 +6,20 @@ import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.Font
import net.minecraft.client.gui.components.events.GuiEventListener
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.renderer.Rect2i
import net.minecraft.network.chat.Component
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.moveMousePosScaled
import ru.dbotthepony.mc.otm.client.render.ScissorRect
import ru.dbotthepony.mc.otm.client.render.currentScissorRect
import ru.dbotthepony.mc.otm.client.render.popScissorRect
import ru.dbotthepony.mc.otm.client.render.pushScissorRect
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import java.util.*
import kotlin.collections.ArrayList
import kotlin.math.max
import kotlin.math.roundToInt
@JvmRecord
data class ScreenPos(val x: Float, val y: Float)
@ -44,6 +45,16 @@ interface ISlotPanel<S : Slot> {
val slot: S
}
interface IItemStackPanel {
val itemStack: ItemStack
}
data class Rect2f(val x: Float, val y: Float, val width: Float, val height: Float) {
fun toIntRect(): Rect2i {
return Rect2i(x.roundToInt(), y.roundToInt(), width.roundToInt(), height.roundToInt())
}
}
open class EditablePanel<out S : Screen> @JvmOverloads constructor(
val screen: S,
parent: EditablePanel<*>?,
@ -262,17 +273,61 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
dockPadding = DockProperty(left, top, right, bottom)
}
// абсолютное начало координат этой панели и потомков
// негативно если потомок выходит за границы
var boundingX = 0f
/**
* Rectangle which hold all children in panel-local (to this panel) coordinates
*/
var childrenRectX = 0f
private set
var boundingY = 0f
/**
* Rectangle which hold all children in panel-local (to this panel) coordinates
*/
var childrenRectY = 0f
private set
var boundingWidth = 0f
/**
* Rectangle which hold all children in panel-local (to this panel) coordinates
*/
var childrenRectWidth = 0f
private set
var boundingHeight = 0f
/**
* Rectangle which hold all children in panel-local (to this panel) coordinates
*/
var childrenRectHeight = 0f
private set
val isOutsideOfParent: Boolean get() {
val parent = parent ?: return false
return x < 0 || y < 0 || x + width > parent.width || y + height > parent.height
}
fun calculateAbsoluteRectangle(): Rect2f {
val x = if (childrenRectX < 0) childrenRectX + absoluteX else absoluteX
val y = if (childrenRectY < 0) childrenRectY + absoluteY else absoluteY
val width = if (childrenRectWidth > width) childrenRectWidth else width
val height = if (childrenRectHeight > height) childrenRectHeight else height
return Rect2f(x, y, width, height)
}
fun calculateAbsoluteRectangles(): List<Rect2f> {
if (childrenRectX >= 0 && childrenRectY >= 0 && childrenRectWidth <= width && childrenRectHeight <= height) {
return listOf(Rect2f(absoluteX, absoluteY, width, height))
}
val result = ArrayList<Rect2f>()
result.add(Rect2f(absoluteX, absoluteY, width, height))
for (children in children) {
if (children.isOutsideOfParent) {
result.addAll(children.calculateAbsoluteRectangles())
}
}
return result
}
// Абсолютная координата, обновляется на отрисовке
var absoluteX = 0f
private set
@ -445,7 +500,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
return false
}
if ((boundingHeight > height || boundingWidth > width || boundingX < 0 || boundingY < 0) && parent == null) {
if ((childrenRectHeight > height || childrenRectWidth > width || childrenRectX < 0 || childrenRectY < 0) && parent == null) {
var hit = false
for (child in children) {
@ -511,6 +566,46 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
return false to null
}
fun findItemStack(mouseX: Float, mouseY: Float, ignoreMouseInputLock: Boolean = false): Pair<Boolean, ItemStack> {
if (!isVisible()) {
return false to ItemStack.EMPTY
}
if (!acceptMouseInput && !ignoreMouseInputLock) {
return (mouseX >= absoluteX &&
mouseX <= absoluteX + width &&
mouseY >= absoluteY &&
mouseY <= absoluteY + height) to ItemStack.EMPTY
}
if (trapMouseInput && this is IItemStackPanel) {
return true to this.itemStack
}
for (child in children) {
val (status, itemStack) = child.findItemStack(mouseX, mouseY, ignoreMouseInputLock)
if (status) {
return true to itemStack
}
}
if (
mouseX >= absoluteX &&
mouseX <= absoluteX + width &&
mouseY >= absoluteY &&
mouseY <= absoluteY + height
) {
if (this is IItemStackPanel) {
return true to this.itemStack
}
return true to ItemStack.EMPTY
}
return false to ItemStack.EMPTY
}
fun renderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (!isVisible()) {
return false
@ -717,19 +812,22 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
fun updateBounds() {
boundsInvalidated = false
boundingX = 0f
boundingY = 0f
boundingWidth = 0f
boundingHeight = 0f
childrenRectX = width
childrenRectY = height
childrenRectWidth = 0f
childrenRectHeight = 0f
for (child in children) {
if (child.isVisible()) {
boundingX = boundingX.coerceAtMost(child.x)
boundingY = boundingY.coerceAtMost(child.y)
boundingWidth = boundingWidth.coerceAtLeast(child.x + child.width)
boundingHeight = boundingHeight.coerceAtLeast(child.y + child.height)
childrenRectX = childrenRectX.coerceAtMost(child.x)
childrenRectY = childrenRectY.coerceAtMost(child.y)
childrenRectWidth = childrenRectWidth.coerceAtLeast(child.x + child.width)
childrenRectHeight = childrenRectHeight.coerceAtLeast(child.y + child.height)
}
}
childrenRectX = childrenRectX.coerceAtMost(childrenRectWidth)
childrenRectY = childrenRectY.coerceAtMost(childrenRectHeight)
}
/**
@ -941,8 +1039,8 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
}
fun withinExtendedBounds(x: Double, y: Double): Boolean {
val pos: ScreenPos = localToScreen(boundingX, boundingY)
return pos.x <= x && pos.x + boundingWidth > x && pos.y <= y && pos.y + boundingHeight > y
val pos: ScreenPos = localToScreen(childrenRectX, childrenRectY)
return pos.x <= x && pos.x + childrenRectWidth > x && pos.y <= y && pos.y + childrenRectHeight > y
}
fun killFocusForEverythingExcept(except: EditablePanel<*>) {

View File

@ -16,7 +16,7 @@ open class FilterSlotPanel<out S : MatteryScreen<*>> @JvmOverloads constructor(
width: Float = SIZE,
height: Float = SIZE
) : AbstractSlotPanel<S>(screen, parent, x, y, width, height) {
override fun getItemStack(): ItemStack {
override val itemStack: ItemStack get() {
return slot.get()
}

View File

@ -44,9 +44,8 @@ open class SlotPanel<out S : MatteryScreen<*>, T : Slot> @JvmOverloads construct
return true
}
override fun getItemStack(): ItemStack {
return slot.item
}
override val itemStack: ItemStack
get() = slot.item
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
renderSlotBackground(stack, mouseX, mouseY, partialTick)

View File

@ -3,20 +3,25 @@ package ru.dbotthepony.mc.otm.compat.jei
import mezz.jei.api.IModPlugin
import mezz.jei.api.JeiPlugin
import mezz.jei.api.constants.RecipeTypes
import mezz.jei.api.gui.handlers.IGuiContainerHandler
import mezz.jei.api.helpers.IJeiHelpers
import mezz.jei.api.registration.IGuiHandlerRegistration
import mezz.jei.api.registration.IRecipeCatalystRegistration
import mezz.jei.api.registration.IRecipeCategoryRegistration
import mezz.jei.api.registration.IRecipeRegistration
import net.minecraft.client.renderer.Rect2i
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.registry.MBlocks
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.registry.MItems
import ru.dbotthepony.mc.otm.registry.MRecipes
import ru.dbotthepony.mc.otm.registry.WriteOnce
import java.util.stream.Collectors
import kotlin.math.roundToInt
var helpers: IJeiHelpers by WriteOnce()
var isJeiLoaded = false
private set
@JeiPlugin
@ -24,6 +29,13 @@ var helpers: IJeiHelpers by WriteOnce()
class JEIPlugin : IModPlugin {
companion object {
private val LOCATION = ResourceLocation(OverdriveThatMatters.MOD_ID, "jei_plugin")
var helpers: IJeiHelpers by WriteOnce()
private set
}
init {
isJeiLoaded = true
}
override fun getPluginUid(): ResourceLocation {
@ -45,4 +57,20 @@ class JEIPlugin : IModPlugin {
registration.addRecipes(PlatePressRecipeCategory.recipeType, level.recipeManager.getAllRecipesFor(MRecipes.PLATE_PRESS).filter { !it.isIncomplete })
}
override fun registerGuiHandlers(registration: IGuiHandlerRegistration) {
registration.addGenericGuiContainerHandler(MatteryScreen::class.java, object : IGuiContainerHandler<MatteryScreen<*>> {
override fun getGuiExtraAreas(containerScreen: MatteryScreen<*>): MutableList<Rect2i> {
return containerScreen.panelsView.stream().map { it.calculateAbsoluteRectangles() }.flatMap { it.stream() }.map { it.toIntRect() }.collect(Collectors.toList())
}
override fun getIngredientUnderMouse(
containerScreen: MatteryScreen<*>,
mouseX: Double,
mouseY: Double
): Any? {
return containerScreen.panelsView.stream().map { it.findItemStack(mouseX.toFloat(), mouseY.toFloat(), ignoreMouseInputLock = true) }.filter { it.first }.findAny().orElse(null)?.second
}
})
}
}

View File

@ -83,7 +83,7 @@ object PlatePressRecipeCategory : IRecipeCategory<PlatePressRecipe>, IDrawable {
}
private val iconField by lazy {
helpers.guiHelper.createDrawableItemStack(ItemStack(MItems.PLATE_PRESS))
JEIPlugin.helpers.guiHelper.createDrawableItemStack(ItemStack(MItems.PLATE_PRESS))
}
override fun getIcon(): IDrawable {