diff --git a/api/UniversalCraft.api b/api/UniversalCraft.api index 276da1fb..d580c79e 100644 --- a/api/UniversalCraft.api +++ b/api/UniversalCraft.api @@ -529,6 +529,7 @@ public final class gg/essential/universal/UKeyboard { public static final field KEY_Y I public static final field KEY_Z I public static final fun allowRepeatEvents (Z)V + public static final fun getKeyModifiers ()Lgg/essential/universal/UKeyboard$Modifiers; public static final fun getKeyName (I)Ljava/lang/String; public static final fun getKeyName (II)Ljava/lang/String; @1.17.1-forge,1.18.1-forge,1.19.2-forge,1.19.3-forge,1.19.4-forge,1.20.1-forge,1.20.2-forge,1.20.4-forge,1.20.4-neoforge,1.20.6-forge,1.20.6-neoforge,1.21-forge,1.21-neoforge,1.21.3-forge,1.21.3-neoforge,1.21.4-forge,1.21.4-neoforge,1.21.5-forge,1.21.5-neoforge,1.21.7-forge,1.21.7-neoforge,26.1-fabric @@ -541,6 +542,8 @@ public final class gg/essential/universal/UKeyboard { public static final fun getKeyName (Lnet/minecraft/client/settings/KeyBinding;)Ljava/lang/String; public static final fun getModifiers ()Lgg/essential/universal/UKeyboard$Modifiers; public static final fun isAltKeyDown ()Z + public static final fun isCommandKeyDown ()Z + public static final fun isControlKeyDown ()Z public static final fun isCtrlKeyDown ()Z public static final fun isEnterKey (I)Z public static final fun isKeyComboCtrlA (I)Z @@ -551,21 +554,27 @@ public final class gg/essential/universal/UKeyboard { public static final fun isKeyComboCtrlY (I)Z public static final fun isKeyComboCtrlZ (I)Z public static final fun isKeyDown (I)Z + public static final fun isOSModifierKeyDown ()Z public static final fun isShiftKeyDown ()Z } public final class gg/essential/universal/UKeyboard$Modifiers { public fun (ZZZ)V + public fun (ZZZZ)V public final fun component1 ()Z public final fun component2 ()Z public final fun component3 ()Z - public final fun copy (ZZZ)Lgg/essential/universal/UKeyboard$Modifiers; - public static synthetic fun copy$default (Lgg/essential/universal/UKeyboard$Modifiers;ZZZILjava/lang/Object;)Lgg/essential/universal/UKeyboard$Modifiers; + public final fun component4 ()Z + public final fun copy (ZZZZ)Lgg/essential/universal/UKeyboard$Modifiers; + public static synthetic fun copy$default (Lgg/essential/universal/UKeyboard$Modifiers;ZZZZILjava/lang/Object;)Lgg/essential/universal/UKeyboard$Modifiers; public fun equals (Ljava/lang/Object;)Z public fun hashCode ()I public final fun isAlt ()Z public final fun isCtrl ()Z + public final fun isOnlyPlatformModifierActive ()Z + public final fun isPlatformModifierActive ()Z public final fun isShift ()Z + public final fun isSuper ()Z public fun toString ()Ljava/lang/String; } @@ -865,6 +874,7 @@ public abstract class gg/essential/universal/UScreen : net/minecraft/client/gui/ @26.1-fabric public final fun extractRenderState (Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V public static final fun getCurrentScreen ()Lnet/minecraft/client/gui/screens/Screen; + protected final fun getInputHandler ()Lgg/essential/universal/UScreen$InputHandler; public fun getNewGuiScale ()I public final fun getRestoreCurrentGuiOnClose ()Z public fun getTitle ()Lnet/minecraft/network/chat/Component; @@ -923,9 +933,11 @@ public abstract class gg/essential/universal/UScreen : net/minecraft/client/gui/ @1.17.1-forge,1.18.1-forge,1.19.2-forge,1.19.3-forge public final fun renderBackground (Lcom/mojang/blaze3d/vertex/PoseStack;I)V public final fun restorePreviousScreen ()V + protected final fun setInputHandler (Lgg/essential/universal/UScreen$InputHandler;)V public fun setNewGuiScale (I)V public fun setUnlocalizedName (Ljava/lang/String;)V public final fun tick ()V + public final fun uSuperInputHandler ()Lgg/essential/universal/UScreen$InputHandler; public fun updateGuiScale ()V } @@ -945,6 +957,7 @@ public abstract class gg/essential/universal/UScreen : net/minecraft/client/gui/ public final fun charTyped (CI)Z public static final fun displayScreen (Lnet/minecraft/client/gui/screen/Screen;)V public static final fun getCurrentScreen ()Lnet/minecraft/client/gui/screen/Screen; + protected final fun getInputHandler ()Lgg/essential/universal/UScreen$InputHandler; public fun getNewGuiScale ()I public final fun getRestoreCurrentGuiOnClose ()Z @1.16.2-forge @@ -1013,9 +1026,11 @@ public abstract class gg/essential/universal/UScreen : net/minecraft/client/gui/ @1.16.2-fabric,1.17.1-fabric,1.18.1-fabric,1.19-fabric,1.19.1-fabric,1.19.2-fabric,1.19.3-fabric public final fun renderBackground (Lnet/minecraft/client/util/math/MatrixStack;I)V public final fun restorePreviousScreen ()V + protected final fun setInputHandler (Lgg/essential/universal/UScreen$InputHandler;)V public fun setNewGuiScale (I)V public fun setUnlocalizedName (Ljava/lang/String;)V public final fun tick ()V + public final fun uSuperInputHandler ()Lgg/essential/universal/UScreen$InputHandler; public fun updateGuiScale ()V } @@ -1033,6 +1048,7 @@ public abstract class gg/essential/universal/UScreen : net/minecraft/client/gui/ public final fun drawScreen (IIF)V public final fun drawWorldBackground (I)V public static final fun getCurrentScreen ()Lnet/minecraft/client/gui/GuiScreen; + protected final fun getInputHandler ()Lgg/essential/universal/UScreen$InputHandler; public fun getNewGuiScale ()I public final fun getRestoreCurrentGuiOnClose ()Z public fun getUnlocalizedName ()Ljava/lang/String; @@ -1059,8 +1075,10 @@ public abstract class gg/essential/universal/UScreen : net/minecraft/client/gui/ public fun onScreenClose ()V public fun onTick ()V public final fun restorePreviousScreen ()V + protected final fun setInputHandler (Lgg/essential/universal/UScreen$InputHandler;)V public fun setNewGuiScale (I)V public fun setUnlocalizedName (Ljava/lang/String;)V + public final fun uSuperInputHandler ()Lgg/essential/universal/UScreen$InputHandler; public fun updateGuiScale ()V public final fun updateScreen ()V } @@ -1080,6 +1098,17 @@ public final class gg/essential/universal/UScreen$Companion { public final fun getCurrentScreen ()Lnet/minecraft/client/gui/GuiScreen; } +public abstract interface class gg/essential/universal/UScreen$InputHandler { + public fun uCharTyped (I)Z + public fun uKeyPressed (IILgg/essential/universal/UKeyboard$Modifiers;)Z + public fun uKeyReleased (IILgg/essential/universal/UKeyboard$Modifiers;)Z + public fun uMouseClicked (DDILgg/essential/universal/UKeyboard$Modifiers;)Z + public fun uMouseDragged (DDILgg/essential/universal/UKeyboard$Modifiers;)Z + public fun uMouseReleased (DDILgg/essential/universal/UKeyboard$Modifiers;)Z + public fun uMouseScrolled (DDDD)Z + public abstract fun uSuperInputHandler ()Lgg/essential/universal/UScreen$InputHandler; +} + public final class gg/essential/universal/USound { public static final field INSTANCE Lgg/essential/universal/USound; public final fun playButtonPress ()V diff --git a/src/main/kotlin/gg/essential/universal/UKeyboard.kt b/src/main/kotlin/gg/essential/universal/UKeyboard.kt index 06caa76c..9d0098fe 100644 --- a/src/main/kotlin/gg/essential/universal/UKeyboard.kt +++ b/src/main/kotlin/gg/essential/universal/UKeyboard.kt @@ -270,40 +270,69 @@ object UKeyboard { //#endif } + @Deprecated("Use isOSModifierKeyDown() instead if you want the platform-appropriate modifier key (Ctrl on Windows/Linux, Command on Mac). " + + "Alternatively use isControlKeyDown() or isCommandKeyDown() if you specifically want to check for Ctrl or Command respectively.") @JvmStatic fun isCtrlKeyDown(): Boolean = if (UMinecraft.isRunningOnMac) { isKeyDown(KEY_LMETA) || isKeyDown(KEY_RMETA) } else isKeyDown(KEY_LCONTROL) || isKeyDown(KEY_RCONTROL) + @Deprecated("If using from a key event, use the provided modifiers instead of calling this method.") + @JvmStatic + fun isOSModifierKeyDown(): Boolean = + if (UMinecraft.isRunningOnMac) isCommandKeyDown() + else isControlKeyDown() + + @Deprecated("If using from a key event, use the provided modifiers instead of calling this method.") + @JvmStatic + fun isCommandKeyDown(): Boolean = isKeyDown(KEY_LMETA) || isKeyDown(KEY_RMETA) + + @Deprecated("If using from a key event, use the provided modifiers instead of calling this method.") + @JvmStatic + fun isControlKeyDown(): Boolean = isKeyDown(KEY_LCONTROL) || isKeyDown(KEY_RCONTROL) + + @Deprecated("If using from a key event, use the provided modifiers instead of calling this method.") @JvmStatic fun isShiftKeyDown(): Boolean = isKeyDown(KEY_LSHIFT) || isKeyDown(KEY_RSHIFT) + @Deprecated("If using from a key event, use the provided modifiers instead of calling this method.") @JvmStatic fun isAltKeyDown(): Boolean = isKeyDown(KEY_LMENU) || isKeyDown(KEY_RMENU) + @Deprecated("Inconsistent ctrl modifier behaviour depending on OS and MC versions", replaceWith = ReplaceWith("getKeyModifiers()")) @JvmStatic - fun getModifiers(): Modifiers = Modifiers(isCtrlKeyDown(), isShiftKeyDown(), isAltKeyDown()) + fun getModifiers(): Modifiers = Modifiers(isCtrlKeyDown(), isShiftKeyDown(), isAltKeyDown(), isCommandKeyDown()) @JvmStatic - fun isKeyComboCtrlA(key: Int): Boolean = key == KEY_A && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown() + fun getKeyModifiers(): Modifiers = Modifiers(isControlKeyDown(), isShiftKeyDown(), isAltKeyDown(), isCommandKeyDown()) + @Deprecated("If using from a key event, use the provided modifiers instead of calling this method.") @JvmStatic - fun isKeyComboCtrlC(key: Int): Boolean = key == KEY_C && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown() + fun isKeyComboCtrlA(key: Int): Boolean = key == KEY_A && isOSModifierKeyDown() && !isShiftKeyDown() && !isAltKeyDown() + @Deprecated("If using from a key event, use the provided modifiers instead of calling this method.") @JvmStatic - fun isKeyComboCtrlV(key: Int): Boolean = key == KEY_V && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown() + fun isKeyComboCtrlC(key: Int): Boolean = key == KEY_C && isOSModifierKeyDown() && !isShiftKeyDown() && !isAltKeyDown() + @Deprecated("If using from a key event, use the provided modifiers instead of calling this method.") @JvmStatic - fun isKeyComboCtrlX(key: Int): Boolean = key == KEY_X && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown() + fun isKeyComboCtrlV(key: Int): Boolean = key == KEY_V && isOSModifierKeyDown() && !isShiftKeyDown() && !isAltKeyDown() + @Deprecated("If using from a key event, use the provided modifiers instead of calling this method.") @JvmStatic - fun isKeyComboCtrlY(key: Int): Boolean = key == KEY_Y && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown() + fun isKeyComboCtrlX(key: Int): Boolean = key == KEY_X && isOSModifierKeyDown() && !isShiftKeyDown() && !isAltKeyDown() + @Deprecated("If using from a key event, use the provided modifiers instead of calling this method.") @JvmStatic - fun isKeyComboCtrlZ(key: Int): Boolean = key == KEY_Z && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown() + fun isKeyComboCtrlY(key: Int): Boolean = key == KEY_Y && isOSModifierKeyDown() && !isShiftKeyDown() && !isAltKeyDown() + @Deprecated("If using from a key event, use the provided modifiers instead of calling this method.") @JvmStatic - fun isKeyComboCtrlShiftZ(key: Int): Boolean = key == KEY_Z && isCtrlKeyDown() && isShiftKeyDown() && !isAltKeyDown() + fun isKeyComboCtrlZ(key: Int): Boolean = key == KEY_Z && isOSModifierKeyDown() && !isShiftKeyDown() && !isAltKeyDown() + + @Deprecated("If using from a key event, use the provided modifiers instead of calling this method.") + @JvmStatic + fun isKeyComboCtrlShiftZ(key: Int): Boolean = key == KEY_Z && isOSModifierKeyDown() && isShiftKeyDown() && !isAltKeyDown() //#if STANDALONE //$$ internal val keysDown = mutableSetOf() @@ -390,19 +419,40 @@ object UKeyboard { @JvmStatic fun getKeyName(keyCode: Int): String? = getKeyName(keyCode, -1) - data class Modifiers(val isCtrl: Boolean, val isShift: Boolean, val isAlt: Boolean) + data class Modifiers( + val isCtrl: Boolean, + val isShift: Boolean, + val isAlt: Boolean, + val isSuper: Boolean, + ) { + @Deprecated("Old constructor") + constructor(isCtrl: Boolean, isShift: Boolean, isAlt: Boolean) : + this(isCtrl, isShift, isAlt, isSuper = isCommandKeyDown()) + + /** + * Checks that only the OS specific modifier key is active (Ctrl on Windows/Linux, Command (super) on Mac). + */ + fun isPlatformModifierActive() = if (UMinecraft.isRunningOnMac) isSuper else isCtrl + + fun isOnlyPlatformModifierActive() = + if (UMinecraft.isRunningOnMac) isSuper && !isShift && !isAlt && !isCtrl + else isCtrl && !isShift && !isAlt && !isSuper + + } //#if MC>=11502 //$$ internal fun Modifiers?.toInt() = listOf( //$$ this?.isCtrl to GLFW.GLFW_MOD_CONTROL, //$$ this?.isShift to GLFW.GLFW_MOD_SHIFT, //$$ this?.isAlt to GLFW.GLFW_MOD_ALT, + //$$ this?.isSuper to GLFW.GLFW_MOD_SUPER, //$$ ).sumOf { (modifier, value) -> if (modifier == true) value else 0 } //$$ //$$ internal fun Int.toModifiers() = Modifiers( //$$ isCtrl = (this and GLFW.GLFW_MOD_CONTROL) != 0, //$$ isShift = (this and GLFW.GLFW_MOD_SHIFT) != 0, //$$ isAlt = (this and GLFW.GLFW_MOD_ALT) != 0, + //$$ isSuper = (this and GLFW.GLFW_MOD_SUPER) != 0, //$$ ) //#endif } diff --git a/src/main/kotlin/gg/essential/universal/UScreen.kt b/src/main/kotlin/gg/essential/universal/UScreen.kt index c22d99eb..8ee221e2 100644 --- a/src/main/kotlin/gg/essential/universal/UScreen.kt +++ b/src/main/kotlin/gg/essential/universal/UScreen.kt @@ -28,9 +28,12 @@ import net.minecraft.client.gui.GuiScreen //#else import org.lwjgl.input.Mouse import java.io.IOException +import kotlin.math.floor //#endif +private const val INPUTHANDLER_DEP_MSG = "Implement [UScreen.InputHandler] for input functions that more closely adhere to newer MC input behaviour." + abstract class UScreen( val restoreCurrentGuiOnClose: Boolean = false, open var newGuiScale: Int = -1, @@ -53,6 +56,7 @@ abstract class UScreen( private var guiScaleToRestore = -1 private var restoringGuiScale = false private val screenToRestore: GuiScreen? = if (restoreCurrentGuiOnClose) currentScreen else null + protected var inputHandler: InputHandler? = this as? InputHandler //#if MC>=12106 //$$ // Background is now draw from the final `renderWithTooltip` method, before we ever get control, so we need //$$ // to suppress by default and can only allow during `onDrawScreen`. @@ -134,14 +138,26 @@ abstract class UScreen( //#endif //$$ } //$$ + //#if MC < 26.1 && MC >= 1.15.2 + //$$ // Smuggle this value for use in super calls, where intermediate functions have dropped it to match 26.1+ + //$$ private var lastCharModifiers = 0 + //#endif //#if MC>=12109 //$$ final override fun keyPressed(input: KeyInput): Boolean { - //$$ onKeyPressed(input.key, 0.toChar(), input.modifiers.toModifiers()) + //$$ inputHandler?.let { + //$$ return it.uKeyPressed(input.key, input.scancode, input.modifiers.toModifiers()) + //$$ } + //$$ + //$$ @Suppress("DEPRECATION") onKeyPressed(input.key, 0.toChar(), input.modifiers.toModifiers()) //$$ return false //$$ } //$$ //$$ final override fun keyReleased(input: KeyInput): Boolean { - //$$ onKeyReleased(input.key, 0.toChar(), input.modifiers.toModifiers()) + //$$ inputHandler?.let { + //$$ return it.uKeyReleased(input.key, input.scancode, input.modifiers.toModifiers()) + //$$ } + //$$ + //$$ @Suppress("DEPRECATION") onKeyReleased(input.key, 0.toChar(), input.modifiers.toModifiers()) //$$ return false //$$ } //$$ @@ -152,11 +168,21 @@ abstract class UScreen( //#else //$$ val modifiers = input.modifiers.toModifiers() //#endif + //$$ inputHandler?.let { + //#if MC < 26.1 + //$$ lastCharModifiers = input.modifiers + //#endif + //$$ return it.uCharTyped(codepoint).also { + //#if MC < 26.1 + //$$ lastCharModifiers = 0 + //#endif + //$$ } + //$$ } //$$ if (Character.isBmpCodePoint(codepoint)) { - //$$ onKeyPressed(0, input.codepoint.toChar(), modifiers) + //$$ @Suppress("DEPRECATION") onKeyPressed(0, input.codepoint.toChar(), modifiers) //$$ } else if (Character.isValidCodePoint(codepoint)) { - //$$ onKeyPressed(0, Character.highSurrogate(input.codepoint), modifiers) - //$$ onKeyPressed(0, Character.lowSurrogate(input.codepoint), modifiers) + //$$ @Suppress("DEPRECATION") onKeyPressed(0, Character.highSurrogate(input.codepoint), modifiers) + //$$ @Suppress("DEPRECATION") onKeyPressed(0, Character.lowSurrogate(input.codepoint), modifiers) //$$ } //$$ return false //$$ } @@ -168,7 +194,15 @@ abstract class UScreen( //$$ lastMouseInput = click.buttonInfo //$$ lastDoubled = doubled //$$ if (click.button() == 1) lastClick = UMinecraft.getTime() - //$$ onMouseClicked(click.x, click.y, click.button()) + //$$ + //$$ inputHandler?.let { + //$$ return it.uMouseClicked(click.x, click.y, click.button(), click.modifiers().toModifiers()).also { + //$$ lastMouseInput = null + //$$ lastDoubled = null + //$$ } + //$$ } + //$$ + //$$ @Suppress("DEPRECATION") onMouseClicked(click.x, click.y, click.button()) //$$ lastMouseInput = null //$$ lastDoubled = null //$$ return false @@ -176,7 +210,14 @@ abstract class UScreen( //$$ //$$ final override fun mouseReleased(click: Click): Boolean { //$$ lastMouseInput = click.buttonInfo - //$$ onMouseReleased(click.x, click.y, click.button()) + //$$ + //$$ inputHandler?.let { + //$$ return it.uMouseReleased(click.x, click.y, click.button(), click.modifiers().toModifiers()).also { + //$$ lastMouseInput = null + //$$ } + //$$ } + //$$ + //$$ @Suppress("DEPRECATION") onMouseReleased(click.x, click.y, click.button()) //$$ lastMouseInput = null //$$ return false //$$ } @@ -185,42 +226,76 @@ abstract class UScreen( //$$ lastMouseInput = click.buttonInfo //$$ lastDraggedDx = offsetX //$$ lastDraggedDy = offsetY - //$$ onMouseDragged(click.x, click.y, click.button(), UMinecraft.getTime() - lastClick) + //$$ + //$$ inputHandler?.let { + //$$ return it.uMouseDragged(click.x, click.y, click.button(), click.modifiers().toModifiers()).also { + //$$ lastMouseInput = null + //$$ } + //$$ } + //$$ + //$$ @Suppress("DEPRECATION") onMouseDragged(click.x, click.y, click.button(), UMinecraft.getTime() - lastClick) //$$ lastMouseInput = null //$$ return false //$$ } //#else //$$ final override fun keyPressed(keyCode: Int, scanCode: Int, modifierCode: Int): Boolean { - //$$ onKeyPressed(keyCode, 0.toChar(), modifierCode.toModifiers()) + //$$ inputHandler?.let { + //$$ return it.uKeyPressed(keyCode, scanCode, modifierCode.toModifiers()) + //$$ } + //$$ + //$$ @Suppress("DEPRECATION") onKeyPressed(keyCode, 0.toChar(), modifierCode.toModifiers()) //$$ return false //$$ } //$$ //$$ final override fun keyReleased(keyCode: Int, scanCode: Int, modifierCode: Int): Boolean { - //$$ onKeyReleased(keyCode, 0.toChar(), modifierCode.toModifiers()) + //$$ inputHandler?.let { + //$$ return it.uKeyReleased(keyCode, scanCode, modifierCode.toModifiers()) + //$$ } + //$$ + //$$ @Suppress("DEPRECATION") onKeyReleased(keyCode, 0.toChar(), modifierCode.toModifiers()) //$$ return false //$$ } //$$ //$$ final override fun charTyped(char: Char, modifierCode: Int): Boolean { - //$$ onKeyPressed(0, char, modifierCode.toModifiers()) + //$$ inputHandler?.let { + //$$ lastCharModifiers = modifierCode + //$$ return it.uCharTyped(char.code).also { lastCharModifiers = 0 } + //$$ } + //$$ + //$$ @Suppress("DEPRECATION") onKeyPressed(0, char, modifierCode.toModifiers()) //$$ return false //$$ } //$$ //$$ final override fun mouseClicked(mouseX: Double, mouseY: Double, mouseButton: Int): Boolean { //$$ if (mouseButton == 1) //$$ lastClick = UMinecraft.getTime() - //$$ onMouseClicked(mouseX, mouseY, mouseButton) + //$$ + //$$ inputHandler?.let { + //$$ return it.uMouseClicked(mouseX, mouseY, mouseButton, UKeyboard.getModifiers()) + //$$ } + //$$ + //$$ @Suppress("DEPRECATION") onMouseClicked(mouseX, mouseY, mouseButton) //$$ return false //$$ } //$$ //$$ final override fun mouseReleased(mouseX: Double, mouseY: Double, mouseButton: Int): Boolean { - //$$ onMouseReleased(mouseX, mouseY, mouseButton) + //$$ inputHandler?.let { + //$$ return it.uMouseReleased(mouseX, mouseY, mouseButton, UKeyboard.getModifiers()) + //$$ } + //$$ + //$$ @Suppress("DEPRECATION") onMouseReleased(mouseX, mouseY, mouseButton) //$$ return false //$$ } //$$ //$$ final override fun mouseDragged(x: Double, y: Double, mouseButton: Int, dx: Double, dy: Double): Boolean { //$$ lastDraggedDx = dx //$$ lastDraggedDy = dy - //$$ onMouseDragged(x, y, mouseButton, UMinecraft.getTime() - lastClick) + //$$ + //$$ inputHandler?.let { + //$$ return it.uMouseDragged(x, y, mouseButton, UKeyboard.getModifiers()) + //$$ } + //$$ + //$$ @Suppress("DEPRECATION") onMouseDragged(x, y, mouseButton, UMinecraft.getTime() - lastClick) //$$ return false //$$ } //#endif @@ -233,8 +308,12 @@ abstract class UScreen( //#endif //$$ lastScrolledX = mouseX //$$ lastScrolledY = mouseY - //$$ @Suppress("DEPRECATION") - //$$ onMouseScrolled(delta) + //$$ + //$$ inputHandler?.let { + //$$ return it.uMouseScrolled(mouseX, mouseY, lastScrolledDX, delta) + //$$ } + //$$ + //$$ @Suppress("DEPRECATION") onMouseScrolled(delta) //$$ return false //$$ } //$$ @@ -289,27 +368,48 @@ abstract class UScreen( } final override fun keyTyped(typedChar: Char, keyCode: Int) { - onKeyPressed(keyCode, typedChar, UKeyboard.getModifiers()) + inputHandler?.let { + val handled = if (keyCode != 0) false else { + it.uKeyPressed(keyCode, 0, UKeyboard.getKeyModifiers()) + } + if (!handled + && !typedChar.isISOControl() // Block control code characters. E.G. the 'CTRL + A' character. https://en.wikipedia.org/wiki/Control_character + && typedChar !in CharCategory.PRIVATE_USE // Block PUA characters. Known to be incorrectly sent by macOS + LWJGL2. https://en.wikipedia.org/wiki/Private_Use_Areas + ) { + it.uCharTyped(typedChar.code) + } + } ?: @Suppress("DEPRECATION") onKeyPressed(keyCode, typedChar, UKeyboard.getKeyModifiers()) } final override fun mouseClicked(mouseX: Int, mouseY: Int, mouseButton: Int) { - onMouseClicked(mouseX.toDouble(), mouseY.toDouble(), mouseButton) + inputHandler?.uMouseClicked(mouseX.restoreFrac(UMouse.Scaled.x), mouseY.restoreFrac(UMouse.Scaled.y), mouseButton, UKeyboard.getKeyModifiers()) + ?: @Suppress("DEPRECATION") onMouseClicked(mouseX.toDouble(), mouseY.toDouble(), mouseButton) } final override fun mouseReleased(mouseX: Int, mouseY: Int, state: Int) { - onMouseReleased(mouseX.toDouble(), mouseY.toDouble(), state) + inputHandler?.uMouseReleased(mouseX.restoreFrac(UMouse.Scaled.x), mouseY.restoreFrac(UMouse.Scaled.y), state, UKeyboard.getKeyModifiers()) + ?: @Suppress("DEPRECATION") onMouseReleased(mouseX.toDouble(), mouseY.toDouble(), state) } final override fun mouseClickMove(mouseX: Int, mouseY: Int, clickedMouseButton: Int, timeSinceLastClick: Long) { - onMouseDragged(mouseX.toDouble(), mouseY.toDouble(), clickedMouseButton, timeSinceLastClick) + inputHandler?.uMouseDragged(mouseX.restoreFrac(UMouse.Scaled.x), mouseY.restoreFrac(UMouse.Scaled.y), clickedMouseButton, UKeyboard.getKeyModifiers()) + ?: @Suppress("DEPRECATION") onMouseDragged(mouseX.toDouble(), mouseY.toDouble(), clickedMouseButton, timeSinceLastClick) } + // We want to restore the fractional part of the mouse click, but not outright override the mouse pos with the real one + // as the input mouse Int may have been modified by another mod. + private fun Int.restoreFrac(realMouseScaled: Double): Double = + this + (realMouseScaled - floor(realMouseScaled)) + final override fun handleMouseInput() { super.handleMouseInput() val scrollDelta = Mouse.getEventDWheel() - @Suppress("DEPRECATION") - if (scrollDelta != 0) - onMouseScrolled(scrollDelta.toDouble()) + if (scrollDelta != 0) { + inputHandler?.uMouseScrolled(UMouse.Scaled.x, UMouse.Scaled.y, 0.0, + // Revert LWJGL 2 delta scaling, see onMouseScrolled(Double) for more info + scrollDelta / 120.0) + ?: @Suppress("DEPRECATION") onMouseScrolled(scrollDelta.toDouble()) + } } final override fun updateScreen() { @@ -404,6 +504,7 @@ abstract class UScreen( onDrawScreen(mouseX, mouseY, partialTicks) } + @Deprecated(INPUTHANDLER_DEP_MSG) open fun onKeyPressed(keyCode: Int, typedChar: Char, modifiers: UKeyboard.Modifiers?) { //#if MC>=11502 //$$ if (keyCode != 0) { @@ -431,6 +532,7 @@ abstract class UScreen( //#endif } + @Deprecated(INPUTHANDLER_DEP_MSG) open fun onKeyReleased(keyCode: Int, typedChar: Char, modifiers: UKeyboard.Modifiers?) { //#if MC>=11502 //$$ if (keyCode != 0) { @@ -443,6 +545,7 @@ abstract class UScreen( //#endif } + @Deprecated(INPUTHANDLER_DEP_MSG) open fun onMouseClicked(mouseX: Double, mouseY: Double, mouseButton: Int) { //#if MC>=11502 //$$ if (mouseButton == 1) @@ -461,6 +564,7 @@ abstract class UScreen( //#endif } + @Deprecated(INPUTHANDLER_DEP_MSG) open fun onMouseReleased(mouseX: Double, mouseY: Double, state: Int) { //#if MC>=12109 //$$ super.mouseReleased(Click(mouseX, mouseY, MouseInput(state, lastMouseInput?.modifiers ?: 0))) @@ -471,6 +575,7 @@ abstract class UScreen( //#endif } + @Deprecated(INPUTHANDLER_DEP_MSG) open fun onMouseDragged(x: Double, y: Double, clickedButton: Int, timeSinceLastClick: Long) { //#if MC>=12109 //$$ super.mouseDragged(Click(x, y, MouseInput(clickedButton, lastMouseInput?.modifiers ?: 0)), lastDraggedDx, lastDraggedDy) @@ -485,6 +590,7 @@ abstract class UScreen( // The deltas obtained from lwjgl 2 are scaled by a constant factor and thus much higher than the ones provided by lwjgl 3. @Deprecated("Provided `delta` values have different units depending on Minecraft versions.", ReplaceWith("onMouseScrolled(mouseX, mouseY, deltaHorizontal, deltaVertical)")) open fun onMouseScrolled(delta: Double) { + @Suppress("DEPRECATION") //#if MC>=11502 //$$ onMouseScrolled(lastScrolledX, lastScrolledY, lastScrolledDX, delta) //#else @@ -499,6 +605,7 @@ abstract class UScreen( // Must be called with consistently scaled deltas on all mc/lwjgl versions. // This is to ensure a consistent scrolling experience across all versions. // See older function above this for further explanation. + @Deprecated(INPUTHANDLER_DEP_MSG) open fun onMouseScrolled(mouseX: Double, mouseY: Double, deltaHorizontal: Double, deltaVertical: Double) { //#if MC>=12002 //$$ super.mouseScrolled(mouseX, mouseY, deltaHorizontal, deltaVertical) @@ -571,6 +678,150 @@ abstract class UScreen( onDrawBackground(tint) } + @Suppress("unused") + private fun superMouseClicked(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean { + //#if MC >= 1.21.9 + //$$ return super.mouseClicked(Click(x, y, MouseInput(button, modifiers.toInt())), lastDoubled ?: false) + //#elseif MC >= 1.15.2 + //$$ return super.mouseClicked(x, y, button) + //#else + super.mouseClicked(x.toInt(), y.toInt(), button) + return false + //#endif + } + + @Suppress("unused") + private fun superMouseReleased(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean { + //#if MC >= 1.21.9 + //$$ return super.mouseReleased(Click(x, y, MouseInput(button, modifiers.toInt()))) + //#elseif MC >= 1.15.2 + //$$ return super.mouseReleased(x, y, button) + //#else + super.mouseReleased(x.toInt(), y.toInt(), button) + return false + //#endif + } + + @Suppress("unused") + private fun superMouseDragged(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean { + //#if MC >= 1.21.9 + //$$ return super.mouseDragged(Click(x, y, MouseInput(button, modifiers.toInt())), lastDraggedDx, lastDraggedDy) + //#elseif MC >= 1.15.2 + //$$ return super.mouseDragged(x, y, button, lastDraggedDx, lastDraggedDy) + //#else + super.mouseClickMove(x.toInt(), y.toInt(), button, 0L) + return false + //#endif + } + + @Suppress("unused") + private fun superMouseScrolled(x: Double, y: Double, scrollX: Double, scrollY: Double): Boolean { + //#if MC >= 1.20.2 + //$$ return super.mouseScrolled(x, y, scrollX, scrollY) + //#elseif MC >= 1.15.2 + //$$ return super.mouseScrolled(x, y, scrollY) + //#else + return false // No super + //#endif + } + + private fun superCharTyped(codepoint: Int): Boolean { + //#if MC >= 26.1 + //$$ return super.charTyped(CharacterEvent(codepoint)) + //#elseif MC >= 1.21.9 + //$$ return super.charTyped(CharInput(codepoint, lastCharModifiers)) + //#elseif MC >= 1.15.2 + //$$ return super.charTyped(codepoint.toChar(), lastCharModifiers) + //#else + super.keyTyped(codepoint.toChar(), 0) + return false + //#endif + } + + @Suppress("unused") + private fun superKeyPressed(key: Int, scanCode: Int, modifiers: UKeyboard.Modifiers): Boolean { + //#if MC >= 1.21.9 + //$$ return super.keyPressed(KeyInput(key, scanCode, modifiers.toInt())) + //#elseif MC >= 1.15.2 + //$$ return super.keyPressed(key, scanCode, modifiers.toInt()) + //#else + super.keyTyped(0.toChar(), key) + return false + //#endif + } + + @Suppress("unused") + private fun superKeyReleased(key: Int, scanCode: Int, modifiers: UKeyboard.Modifiers): Boolean { + //#if MC >= 1.21.9 + //$$ return super.keyReleased(KeyInput(key, scanCode, modifiers.toInt())) + //#elseif MC >= 1.15.2 + //$$ return super.keyReleased(key, scanCode, modifiers.toInt()) + //#else + return false // No super + //#endif + } + + @Suppress("unused") // Becomes used if the child class is an instance of [InputHandler] + fun uSuperInputHandler(): InputHandler = object : InputHandler { + override fun uSuperInputHandler(): InputHandler = this + + override fun uMouseClicked(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean = + superMouseClicked(x, y, button, modifiers) + + override fun uMouseReleased(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean = + superMouseReleased(x, y, button, modifiers) + + override fun uMouseDragged(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean = + superMouseDragged(x, y, button, modifiers) + + override fun uMouseScrolled(x: Double, y: Double, scrollX: Double, scrollY: Double): Boolean = + superMouseScrolled(x, y, scrollX, scrollY) + + override fun uCharTyped(codepoint: Int): Boolean = + superCharTyped(codepoint) + + override fun uKeyPressed(key: Int, scanCode: Int, modifiers: UKeyboard.Modifiers): Boolean = + superKeyPressed(key, scanCode, modifiers) + + override fun uKeyReleased(key: Int, scanCode: Int, modifiers: UKeyboard.Modifiers): Boolean = + superKeyReleased(key, scanCode, modifiers) + } + + /** Usually you can simply have your screen implement this interface, [UScreen] will then use it automatically. + * If you require more control, you can instead also manually set the [inputHandler] property. + * [UScreen] provides a [uSuperInputHandler] implementation you can call from your handler. + * + * On versions below 1.16, the boolean returns are not passed to Minecraft as they are not used, + * the interface still replaces and executes the same for consistency. + */ + interface InputHandler { + fun uSuperInputHandler(): InputHandler + + fun uMouseClicked(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean = + uSuperInputHandler().uMouseClicked(x, y, button, modifiers) + + fun uMouseReleased(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean = + uSuperInputHandler().uMouseReleased(x, y, button, modifiers) + + fun uMouseDragged(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean = + uSuperInputHandler().uMouseDragged(x, y, button, modifiers) + + // Must be called with consistently scaled scroll deltas on all mc/lwjgl versions. + // This is to ensure a consistent scrolling experience across all versions. + // See onMouseScrolled(Double) for further explanation. + fun uMouseScrolled(x: Double, y: Double, scrollX: Double, scrollY: Double): Boolean = + uSuperInputHandler().uMouseScrolled(x, y, scrollX, scrollY) + + fun uCharTyped(codepoint: Int): Boolean = + uSuperInputHandler().uCharTyped(codepoint) + + fun uKeyPressed(key: Int, scanCode: Int, modifiers: UKeyboard.Modifiers): Boolean = + uSuperInputHandler().uKeyPressed(key, scanCode, modifiers) + + fun uKeyReleased(key: Int, scanCode: Int, modifiers: UKeyboard.Modifiers): Boolean = + uSuperInputHandler().uKeyReleased(key, scanCode, modifiers) + } + companion object { @JvmStatic val currentScreen: GuiScreen? diff --git a/standalone/src/main/kotlin/gg/essential/universal/UScreen.kt b/standalone/src/main/kotlin/gg/essential/universal/UScreen.kt index 3a26229c..b2828427 100644 --- a/standalone/src/main/kotlin/gg/essential/universal/UScreen.kt +++ b/standalone/src/main/kotlin/gg/essential/universal/UScreen.kt @@ -13,6 +13,10 @@ abstract class UScreen( private var guiScaleToRestore = -1 private val screenToRestore: UScreen? = if (restoreCurrentGuiOnClose) currentScreen else null + protected var inputHandler: InputHandler? = this as? InputHandler + + internal val inputHandlerInternal: InputHandler? + get() = inputHandler fun initGui() { updateGuiScale() @@ -79,6 +83,42 @@ abstract class UScreen( open fun onDrawBackground(matrixStack: UMatrixStack, tint: Int) { } + fun uSuperInputHandler(): InputHandler = object : InputHandler { + override fun uSuperInputHandler(): InputHandler = this + override fun uMouseClicked(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean = false + override fun uMouseReleased(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean = false + override fun uMouseDragged(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean = false + override fun uMouseScrolled(x: Double, y: Double, scrollX: Double, scrollY: Double): Boolean = false + override fun uCharTyped(codepoint: Int): Boolean = false + override fun uKeyPressed(key: Int, scanCode: Int, modifiers: UKeyboard.Modifiers): Boolean = false + override fun uKeyReleased(key: Int, scanCode: Int, modifiers: UKeyboard.Modifiers): Boolean = false + } + + interface InputHandler { + fun uSuperInputHandler(): InputHandler + + fun uMouseClicked(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean = + uSuperInputHandler().uMouseClicked(x, y, button, modifiers) + + fun uMouseReleased(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean = + uSuperInputHandler().uMouseReleased(x, y, button, modifiers) + + fun uMouseDragged(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean = + uSuperInputHandler().uMouseDragged(x, y, button, modifiers) + + fun uMouseScrolled(x: Double, y: Double, scrollX: Double, scrollY: Double): Boolean = + uSuperInputHandler().uMouseScrolled(x, y, scrollX, scrollY) + + fun uCharTyped(codepoint: Int): Boolean = + uSuperInputHandler().uCharTyped(codepoint) + + fun uKeyPressed(key: Int, scanCode: Int, modifiers: UKeyboard.Modifiers): Boolean = + uSuperInputHandler().uKeyPressed(key, scanCode, modifiers) + + fun uKeyReleased(key: Int, scanCode: Int, modifiers: UKeyboard.Modifiers): Boolean = + uSuperInputHandler().uKeyReleased(key, scanCode, modifiers) + } + companion object { var currentScreen: UScreen? = null private set diff --git a/standalone/src/main/kotlin/gg/essential/universal/standalone/UCWindow.kt b/standalone/src/main/kotlin/gg/essential/universal/standalone/UCWindow.kt index aeb1ec9c..ca75350d 100644 --- a/standalone/src/main/kotlin/gg/essential/universal/standalone/UCWindow.kt +++ b/standalone/src/main/kotlin/gg/essential/universal/standalone/UCWindow.kt @@ -39,17 +39,25 @@ class UCWindow(val glfwWindow: GlfwWindow, val uiScope: CoroutineScope) { } } - GLFW.glfwSetMouseButtonCallback(glfwWindow.glfwId) { _, button, action, _ -> + GLFW.glfwSetMouseButtonCallback(glfwWindow.glfwId) { _, button, action, modifiers -> uiScope.launch { when (action) { GLFW.GLFW_PRESS -> { UKeyboard.keysDown.add(button) - UScreen.currentScreen?.onMouseClicked(UMouse.Scaled.x, UMouse.Scaled.y, button) + UScreen.currentScreen?.run { + inputHandlerInternal + ?.uMouseClicked(UMouse.Scaled.x, UMouse.Scaled.y, button, modifiers.toModifiers()) + ?: onMouseClicked(UMouse.Scaled.x, UMouse.Scaled.y, button) + } } GLFW.GLFW_RELEASE -> { UKeyboard.keysDown.remove(button) - UScreen.currentScreen?.onMouseReleased(UMouse.Scaled.x, UMouse.Scaled.y, button) + UScreen.currentScreen?.run { + inputHandlerInternal + ?.uMouseReleased(UMouse.Scaled.x, UMouse.Scaled.y, button, modifiers.toModifiers()) + ?: onMouseReleased(UMouse.Scaled.x, UMouse.Scaled.y, button) + } } } } @@ -57,29 +65,45 @@ class UCWindow(val glfwWindow: GlfwWindow, val uiScope: CoroutineScope) { GLFW.glfwSetScrollCallback(glfwWindow.glfwId) { _, x, y -> uiScope.launch { - UScreen.currentScreen?.onMouseScrolled(UMouse.Scaled.x, UMouse.Scaled.y, x, y) + UScreen.currentScreen?.run { + inputHandlerInternal + ?.uMouseScrolled(UMouse.Scaled.x, UMouse.Scaled.y, x, y) + ?: onMouseScrolled(UMouse.Scaled.x, UMouse.Scaled.y, x, y) + } } } GLFW.glfwSetCharModsCallback(glfwWindow.glfwId) { _, codepoint, modifiers -> uiScope.launch { for (char in Character.toChars(codepoint)) { - UScreen.currentScreen?.onKeyPressed(0, char, modifiers.toModifiers()) + UScreen.currentScreen?.run { + inputHandlerInternal + ?.uCharTyped(codepoint) + ?: onKeyPressed(0, char, modifiers.toModifiers()) + } } } } - GLFW.glfwSetKeyCallback(glfwWindow.glfwId) { _, key, _, action, modifiers -> + GLFW.glfwSetKeyCallback(glfwWindow.glfwId) { _, key, scancode, action, modifiers -> uiScope.launch { when (action) { GLFW.GLFW_PRESS, GLFW.GLFW_REPEAT -> { UKeyboard.keysDown.add(key) - UScreen.currentScreen?.onKeyPressed(key, 0.toChar(), modifiers.toModifiers()) + UScreen.currentScreen?.run { + inputHandlerInternal + ?.uKeyPressed(key, scancode, modifiers.toModifiers()) + ?: onKeyPressed(key, 0.toChar(), modifiers.toModifiers()) + } } GLFW.GLFW_RELEASE -> { UKeyboard.keysDown.remove(key) - UScreen.currentScreen?.onKeyReleased(key, 0.toChar(), modifiers.toModifiers()) + UScreen.currentScreen?.run { + inputHandlerInternal + ?.uKeyReleased(key, scancode, modifiers.toModifiers()) + ?: onKeyReleased(key, 0.toChar(), modifiers.toModifiers()) + } } } }