From 739a48d4679ce05c1f555a04fcdaa4e509d59d5d Mon Sep 17 00:00:00 2001 From: Thunderblade73 <85900443+Thunderblade73@users.noreply.github.com> Date: Sun, 23 Jun 2024 20:18:45 +0200 Subject: Backend: Drag and Drop for Renderables (#1864) Co-authored-by: Cal Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../hannibal2/skyhanni/events/GuiContainerEvent.kt | 12 ++- .../features/inventory/wardrobe/CustomWardrobe.kt | 2 +- .../skyhanni/mixins/hooks/GuiContainerHook.kt | 7 +- .../mixins/transformers/gui/MixinGuiContainer.java | 5 + .../skyhanni/test/SkyHanniDebugsAndTests.kt | 47 ++++++++- .../skyhanni/utils/renderables/DragNDrop.kt | 106 +++++++++++++++++++++ .../skyhanni/utils/renderables/Renderable.kt | 2 +- .../skyhanni/utils/renderables/RenderableUtils.kt | 7 ++ 8 files changed, 181 insertions(+), 7 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/renderables/DragNDrop.kt (limited to 'src/main/java/at') diff --git a/src/main/java/at/hannibal2/skyhanni/events/GuiContainerEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/GuiContainerEvent.kt index 9837e1529..34bc2a2a5 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/GuiContainerEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/GuiContainerEvent.kt @@ -19,7 +19,7 @@ abstract class GuiContainerEvent(open val gui: GuiContainer, open val container: ) : GuiContainerEvent(gui, container) @Cancelable - data class BeforeDraw( + data class PreDraw( override val gui: GuiContainer, override val container: Container, val mouseX: Int, @@ -30,6 +30,14 @@ abstract class GuiContainerEvent(open val gui: GuiContainer, open val container: GuiRenderUtils.drawGradientRect(0, 0, gui.width, gui.height, -1072689136, -804253680, 0.0) } + data class PostDraw( + override val gui: GuiContainer, + override val container: Container, + val mouseX: Int, + val mouseY: Int, + val partialTicks: Float, + ) : GuiContainerEvent(gui, container) + @Cancelable data class CloseWindowEvent(override val gui: GuiContainer, override val container: Container) : GuiContainerEvent(gui, container) @@ -78,7 +86,7 @@ abstract class GuiContainerEvent(open val gui: GuiContainer, open val container: if (this.clickedButton == 2 && this.clickTypeEnum == ClickType.MIDDLE) return slot?.slotNumber?.let { slotNumber -> Minecraft.getMinecraft().playerController.windowClick( - container.windowId, slotNumber, 2, 3, Minecraft.getMinecraft().thePlayer + container.windowId, slotNumber, 2, 3, Minecraft.getMinecraft().thePlayer, ) isCanceled = true } diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/CustomWardrobe.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/CustomWardrobe.kt index 1c3f36aeb..08abfa34d 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/CustomWardrobe.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/CustomWardrobe.kt @@ -58,7 +58,7 @@ object CustomWardrobe { private var guiName = "Custom Wardrobe" @SubscribeEvent - fun onGuiRender(event: GuiContainerEvent.BeforeDraw) { + fun onGuiRender(event: GuiContainerEvent.PreDraw) { if (!isEnabled() || editMode) return val renderable = displayRenderable ?: run { update() diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/GuiContainerHook.kt b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/GuiContainerHook.kt index 041457d25..552b16f00 100644 --- a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/GuiContainerHook.kt +++ b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/GuiContainerHook.kt @@ -36,7 +36,7 @@ class GuiContainerHook(guiAny: Any) { ci: CallbackInfo, ) { if (!SkyHanniDebugsAndTests.globalRender) return - if (GuiContainerEvent.BeforeDraw(gui, gui.inventorySlots, mouseX, mouseY, partialTicks).postAndCatch()) { + if (GuiContainerEvent.PreDraw(gui, gui.inventorySlots, mouseX, mouseY, partialTicks).postAndCatch()) { NEUApi.setInventoryButtonsToDisabled() GuiData.preDrawEventCancelled = true ci.cancel() @@ -47,6 +47,11 @@ class GuiContainerHook(guiAny: Any) { } } + fun postDraw(mouseX: Int, mouseY: Int, partialTicks: Float) { + if (!SkyHanniDebugsAndTests.globalRender) return + GuiContainerEvent.PostDraw(gui, gui.inventorySlots, mouseX, mouseY, partialTicks).postAndCatch() + } + fun foregroundDrawn(mouseX: Int, mouseY: Int, partialTicks: Float) { GuiContainerEvent.ForegroundDrawnEvent(gui, gui.inventorySlots, mouseX, mouseY, partialTicks).postAndCatch() } diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/gui/MixinGuiContainer.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/gui/MixinGuiContainer.java index c92f2ab7c..acec9ffac 100644 --- a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/gui/MixinGuiContainer.java +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/gui/MixinGuiContainer.java @@ -36,6 +36,11 @@ public abstract class MixinGuiContainer extends GuiScreen { skyHanni$hook.preDraw(mouseX, mouseY, partialTicks, ci); } + @Inject(method = "drawScreen", at = @At("TAIL")) + private void postDraw(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) { + skyHanni$hook.postDraw(mouseX, mouseY, partialTicks); + } + @Inject(method = "drawScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/inventory/GuiContainer;drawGuiContainerForegroundLayer(II)V", shift = At.Shift.AFTER)) private void onForegroundDraw(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) { skyHanni$hook.foregroundDrawn(mouseX, mouseY, partialTicks); diff --git a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt index f3fe16222..e718593f3 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt @@ -56,8 +56,11 @@ import at.hannibal2.skyhanni.utils.RenderUtils.renderString import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems import at.hannibal2.skyhanni.utils.SimpleTimeMark import at.hannibal2.skyhanni.utils.SoundUtils +import at.hannibal2.skyhanni.utils.renderables.DragNDrop +import at.hannibal2.skyhanni.utils.renderables.Droppable import at.hannibal2.skyhanni.utils.renderables.Renderable import at.hannibal2.skyhanni.utils.renderables.Renderable.Companion.renderBounds +import at.hannibal2.skyhanni.utils.renderables.toDragItem import kotlinx.coroutines.launch import net.minecraft.client.Minecraft import net.minecraft.init.Blocks @@ -202,7 +205,7 @@ object SkyHanniDebugsAndTests { onClick = { resetConfig() }, "§eClick to confirm.", prefix = false, - oneTimeClick = true + oneTimeClick = true, ) } @@ -535,6 +538,45 @@ object SkyHanniDebugsAndTests { config.debugPos.renderStringsAndItems(displayList, posLabel = "Test Display") } + @SubscribeEvent + fun onGuiRenderChestGuiOverlayRender(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) { + @Suppress("ConstantConditionIf") + if (false) { + dragAbleTest() + } + } + + private fun dragAbleTest() { + val bone = ItemStack(Items.bone, 1).toDragItem() + val leaf = ItemStack(Blocks.leaves, 1).toDragItem() + + config.debugItemPos.renderRenderables( + listOf( + DragNDrop.draggable(Renderable.string("A Bone"), { bone }), + Renderable.placeholder(0, 30), + DragNDrop.draggable(Renderable.string("A Leaf"), { leaf }), + Renderable.placeholder(0, 30), + DragNDrop.droppable( + Renderable.string("Feed Dog"), + object : Droppable { + override fun handle(drop: Any?) { + val unit = drop as ItemStack + if (unit.item == Items.bone) { + LorenzDebug.chatAndLog("Oh, a bone!") + } else { + LorenzDebug.chatAndLog("Disgusting that is not a bone!") + } + } + + override fun validTarget(item: Any?) = item is ItemStack + + }, + ), + ), + posLabel = "Item Debug", + ) + } + private fun itemRenderDebug() { val scale = 0.1 val renderables = listOf( @@ -547,7 +589,8 @@ object SkyHanniDebugsAndTests { }.editCopy { this.add( 0, - generateSequence(scale) { it + 0.1 }.take(25).map { Renderable.string(it.round(1).toString()) }.toList(), + generateSequence(scale) { it + 0.1 }.take(25).map { Renderable.string(it.round(1).toString()) } + .toList(), ) } config.debugItemPos.renderRenderables( diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/DragNDrop.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/DragNDrop.kt new file mode 100644 index 000000000..70cab119e --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/DragNDrop.kt @@ -0,0 +1,106 @@ +package at.hannibal2.skyhanni.utils.renderables + +import at.hannibal2.skyhanni.events.GuiContainerEvent +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyHeld +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.init.Blocks +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +@SkyHanniModule +object DragNDrop { + + private var currentDrag: DragItem<*>? = null + + private var isInvalidDrop = false + + private const val BUTTON = 0 + + private const val BUTTON_MAPPED = BUTTON - 100 + + private val invalidItem = Renderable.itemStack(ItemStack(Blocks.barrier), 1.0) + + @SubscribeEvent + fun onGuiContainerBeforeDraw(event: GuiContainerEvent.PreDraw) { + isInvalidDrop = false + } + + @SubscribeEvent + fun onGuiContainerAfterDraw(event: GuiContainerEvent.PostDraw) { + val item = currentDrag ?: return + if (!BUTTON_MAPPED.isKeyHeld()) { + currentDrag = null + return + } + GlStateManager.translate(event.mouseX.toFloat(), event.mouseY.toFloat(), 0f) + if (isInvalidDrop) { + invalidItem.render(event.mouseX, event.mouseY) + } else { + item.onRender(event.mouseX, event.mouseY) + } + GlStateManager.translate(-event.mouseX.toFloat(), -event.mouseY.toFloat(), 0f) + } + + fun draggable( + display: Renderable, + item: () -> DragItem<*>, + bypassChecks: Boolean = false, + condition: () -> Boolean = { true }, + ) = Renderable.clickable( + display, + onClick = { currentDrag = item() }, + button = BUTTON, + bypassChecks = bypassChecks, + condition = condition, + ) + + fun droppable( + display: Renderable, + drop: Droppable, + bypassChecks: Boolean = false, + condition: () -> Boolean = { true }, + ): Renderable = object : RenderableWrapper(display) { + override fun render(posX: Int, posY: Int) { + if (isHovered(posX, posY) && condition() && Renderable.shouldAllowLink(true, bypassChecks)) { + handelDroppable(drop) + } + content.render(posX, posY) + } + } + + private fun handelDroppable(drop: Droppable) { + val item = currentDrag ?: return + if (drop.validTarget(item.get())) { + if (!BUTTON_MAPPED.isKeyHeld()) { + drop.handle(item.get()) + currentDrag = null + } + } else { + isInvalidDrop = true + } + + } +} + +fun ItemStack.toDragItem(scale: Double = 1.0) = object : DragItem { + + val render = Renderable.itemStack(this@toDragItem, scale, 0) + + override fun get(): ItemStack = this@toDragItem + + override fun onRender(mouseX: Int, mouseY: Int) = render.render(mouseX, mouseY) +} + +interface DragItem { + + fun get(): T + fun onRender(mouseX: Int, mouseY: Int) + +} + +interface Droppable { + + fun handle(drop: Any?) + fun validTarget(item: Any?): Boolean +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt index f00cba6c5..0ed562152 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt @@ -233,7 +233,7 @@ interface Renderable { } } - private fun shouldAllowLink(debug: Boolean = false, bypassChecks: Boolean): Boolean { + internal fun shouldAllowLink(debug: Boolean = false, bypassChecks: Boolean): Boolean { val guiScreen = Minecraft.getMinecraft().currentScreen val isGuiScreen = guiScreen != null diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt index d1a8aae02..a78044d72 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt @@ -73,3 +73,10 @@ internal object RenderableUtils { return yOffset } } + +internal abstract class RenderableWrapper internal constructor(protected val content: Renderable) : Renderable { + override val width = content.width + override val height = content.height + override val horizontalAlign = content.horizontalAlign + override val verticalAlign = content.verticalAlign +} -- cgit