From e6d5e073d8f569fd178a2c8e9c3c02b226ce0fc2 Mon Sep 17 00:00:00 2001 From: artdeell Date: Sat, 6 Apr 2024 15:16:28 -0400 Subject: [PATCH] Fix[input]: make hotbar behave more like an android button, move hotbar handling into a separate class --- .../customcontrols/mouse/HotbarTracker.java | 114 ++++++++++++++++++ .../mouse/InGameEventProcessor.java | 95 +++------------ 2 files changed, 130 insertions(+), 79 deletions(-) create mode 100644 app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/HotbarTracker.java diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/HotbarTracker.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/HotbarTracker.java new file mode 100644 index 000000000..c789e07cd --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/HotbarTracker.java @@ -0,0 +1,114 @@ +package net.kdt.pojavlaunch.customcontrols.mouse; + +import android.view.MotionEvent; + +import net.kdt.pojavlaunch.LwjglGlfwKeycode; +import net.kdt.pojavlaunch.prefs.LauncherPreferences; +import net.kdt.pojavlaunch.utils.MCOptionUtils; + +import org.lwjgl.glfw.CallbackBridge; + +import static net.kdt.pojavlaunch.utils.MCOptionUtils.getMcScale; + +import android.os.Handler; + +public class HotbarTracker implements MCOptionUtils.MCOptionListener { + + private static final int[] HOTBAR_KEYS = { + LwjglGlfwKeycode.GLFW_KEY_1, LwjglGlfwKeycode.GLFW_KEY_2, LwjglGlfwKeycode.GLFW_KEY_3, + LwjglGlfwKeycode.GLFW_KEY_4, LwjglGlfwKeycode.GLFW_KEY_5, LwjglGlfwKeycode.GLFW_KEY_6, + LwjglGlfwKeycode.GLFW_KEY_7, LwjglGlfwKeycode.GLFW_KEY_8, LwjglGlfwKeycode.GLFW_KEY_9}; + private int mLastHudKey, mHudPointerId; + private final DropGesture mDropGesture; + private int mBarWidth, mBarHeight; + private final float mScaleFactor; + + /** + * @param mGestureHandler the gesture handler for the integrated drop gesture + * @param mScaleFactor the screen scale factor + */ + public HotbarTracker(Handler mGestureHandler, float mScaleFactor) { + computeBarDimensions(); + MCOptionUtils.addMCOptionListener(this); + this.mScaleFactor = mScaleFactor; + mDropGesture = new DropGesture(mGestureHandler); + mHudPointerId = -1; + } + + public boolean begin(MotionEvent motionEvent) { + if(mHudPointerId != -1) return false; + int pointer = motionEvent.getActionIndex(); + if(motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) pointer = 0; + int x = (int)motionEvent.getX(pointer); + if(isWithinHotbar(x, (int)motionEvent.getY(pointer))) { + mHudPointerId = motionEvent.getPointerId(pointer); + hotbarClick(x); + mDropGesture.submit(true); + return true; + }else { + mHudPointerId = -1; + return false; + } + } + + public boolean track(MotionEvent motionEvent, int trackedIndex, boolean hasDoubleTapped) { + if(mHudPointerId == -1) return false; + int index = motionEvent.findPointerIndex(mHudPointerId); + if(index == -1) { + cancel(); + return false; + } + int x = (int)motionEvent.getX(index); + if(isWithinHotbar(x, (int)motionEvent.getY(index))) { + hotbarClick(x); + mDropGesture.submit(true); + if(hasDoubleTapped && !LauncherPreferences.PREF_DISABLE_SWAP_HAND) { + CallbackBridge.sendKeyPress(LwjglGlfwKeycode.GLFW_KEY_F); + } + }else { + mDropGesture.submit(false); + } + return trackedIndex == index; + } + + public void cancel() { + mDropGesture.cancel(); + mHudPointerId = -1; + } + + private boolean isWithinHotbar(int x, int y) { + int barY = CallbackBridge.physicalHeight - mBarHeight; + if(y < barY) return false; + + int barX = (CallbackBridge.physicalWidth / 2) - (mBarWidth / 2); + return x >= barX && x < barX + mBarWidth; + } + + private void hotbarClick(int x) { + int barX = (CallbackBridge.physicalWidth / 2) - (mBarWidth / 2); + if(x < barX || x >= barX + mBarWidth) return; + + int key = HOTBAR_KEYS[(int) net.kdt.pojavlaunch.utils.MathUtils.map(x, barX, barX + mBarWidth, 0, 9)]; + if(key != mLastHudKey) { + CallbackBridge.sendKeyPress(key); + // The GUI bar is handled before the gesture will be submitted, so this + // will be resubmitted again soon (with the timer restarted) + mDropGesture.cancel(); + mLastHudKey = key; + } + } + + private int mcScale(int input, int guiScale) { + return (int)((guiScale * input)/ mScaleFactor); + } + + private void computeBarDimensions() { + int guiScale = getMcScale(); + mBarHeight = mcScale(20, guiScale); + mBarWidth = mcScale(180, guiScale); + } + @Override + public void onOptionChanged() { + computeBarDimensions(); + } +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/InGameEventProcessor.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/InGameEventProcessor.java index f42afde19..975523482 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/InGameEventProcessor.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/InGameEventProcessor.java @@ -1,68 +1,47 @@ package net.kdt.pojavlaunch.customcontrols.mouse; -import static net.kdt.pojavlaunch.utils.MCOptionUtils.getMcScale; - import android.os.Handler; import android.os.Looper; import android.view.MotionEvent; -import net.kdt.pojavlaunch.LwjglGlfwKeycode; import net.kdt.pojavlaunch.TapDetector; import net.kdt.pojavlaunch.prefs.LauncherPreferences; -import net.kdt.pojavlaunch.utils.MCOptionUtils; import org.lwjgl.glfw.CallbackBridge; public class InGameEventProcessor implements TouchEventProcessor { - - private int mGuiScale; - @SuppressWarnings("FieldCanBeLocal") // it can't, otherwise the weak reference will disappear - private final MCOptionUtils.MCOptionListener mGuiScaleListener = () -> mGuiScale = getMcScale(); private final Handler mGestureHandler = new Handler(Looper.getMainLooper()); - private static final int[] HOTBAR_KEYS = { - LwjglGlfwKeycode.GLFW_KEY_1, LwjglGlfwKeycode.GLFW_KEY_2, LwjglGlfwKeycode.GLFW_KEY_3, - LwjglGlfwKeycode.GLFW_KEY_4, LwjglGlfwKeycode.GLFW_KEY_5, LwjglGlfwKeycode.GLFW_KEY_6, - LwjglGlfwKeycode.GLFW_KEY_7, LwjglGlfwKeycode.GLFW_KEY_8, LwjglGlfwKeycode.GLFW_KEY_9}; - private int mLastHudKey; - private final float mScaleFactor; private final double mSensitivity; private final PointerTracker mTracker = new PointerTracker(); + private final HotbarTracker mGuiBarTracker; private final LeftClickGesture mLeftClickGesture = new LeftClickGesture(mGestureHandler); private final RightClickGesture mRightClickGesture = new RightClickGesture(mGestureHandler); - private final DropGesture mDropGesture = new DropGesture(mGestureHandler); private final TapDetector mDoubleTapDetector = new TapDetector(2, TapDetector.DETECTION_METHOD_DOWN); public InGameEventProcessor(float scaleFactor, double sensitivity) { - MCOptionUtils.addMCOptionListener(mGuiScaleListener); - mScaleFactor = scaleFactor; + mGuiBarTracker = new HotbarTracker(mGestureHandler, scaleFactor); mSensitivity = sensitivity; } @Override public boolean processTouchEvent(MotionEvent motionEvent) { boolean hasDoubleTapped = mDoubleTapDetector.onTouchEvent(motionEvent); - boolean hasGuiBarHit = handleGuiBar(motionEvent); - // Handle this gesture separately, outside of the event masking to avoid inconsistencies - // with the double tap detector. - if(hasGuiBarHit && hasDoubleTapped && !LauncherPreferences.PREF_DISABLE_SWAP_HAND) { - CallbackBridge.sendKeyPress(LwjglGlfwKeycode.GLFW_KEY_F); - } - // Handle the rest of the in-game motion. switch (motionEvent.getActionMasked()) { case MotionEvent.ACTION_DOWN: + if(mGuiBarTracker.begin(motionEvent)) break; mTracker.startTracking(motionEvent); if(LauncherPreferences.PREF_DISABLE_GESTURES) break; - checkGestures(handleGuiBar(motionEvent)); + checkGestures(); + break; + case MotionEvent.ACTION_POINTER_DOWN: + mGuiBarTracker.begin(motionEvent); break; case MotionEvent.ACTION_MOVE: - mTracker.trackEvent(motionEvent); - if(!LauncherPreferences.PREF_DISABLE_GESTURES) { - checkGestures(hasGuiBarHit); - } - // Only send new mouse positions if the event hasn't hit the inventory bar. - // Note that the events are sent to the tracker regardless, to prevent cursor - // jumps when leaving the inventory bar in one single touch gesture. - if(hasGuiBarHit) break; + int trackedIndex = mTracker.trackEvent(motionEvent); + // Don't send mouse positions if there's a finger in the gui bar *and* the camera tracker + // tracks the same finger as the gui bar. + if(mGuiBarTracker.track(motionEvent, trackedIndex, hasDoubleTapped)) break; + checkGestures(); float[] motionVector = mTracker.getMotionVector(); CallbackBridge.mouseX += motionVector[0] * mSensitivity; CallbackBridge.mouseY += motionVector[1] * mSensitivity; @@ -70,6 +49,7 @@ public class InGameEventProcessor implements TouchEventProcessor { break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: + mGuiBarTracker.cancel(); mTracker.cancelTracking(); cancelGestures(false); } @@ -81,56 +61,13 @@ public class InGameEventProcessor implements TouchEventProcessor { cancelGestures(true); } - private void checkGestures(boolean hasGuiBarHit) { - if(!hasGuiBarHit) { - mLeftClickGesture.inputEvent(); - mRightClickGesture.inputEvent(); - } - mDropGesture.submit(hasGuiBarHit); + private void checkGestures() { + mLeftClickGesture.inputEvent(); + mRightClickGesture.inputEvent(); } private void cancelGestures(boolean isSwitching) { mLeftClickGesture.cancel(isSwitching); mRightClickGesture.cancel(isSwitching); - mDropGesture.cancel(); - } - - private boolean handleGuiBar(MotionEvent motionEvent) { - int hudKeyHandled = -1; - for(int i = 0; i < motionEvent.getPointerCount(); i++) { - hudKeyHandled = handleGuiBar( - (int)motionEvent.getX(i), (int)motionEvent.getY(i) - ); - if(hudKeyHandled != -1) break; - } - boolean hasGuiBarHit = hudKeyHandled != -1; - if(hasGuiBarHit && hudKeyHandled != mLastHudKey) { - CallbackBridge.sendKeyPress(hudKeyHandled); - // The GUI bar is handled before the gesture will be submitted, so this - // will be resubmitted again soon (with the timer restarted) - mDropGesture.cancel(); - mLastHudKey = hudKeyHandled; - } - return hasGuiBarHit; - } - - /** @return the hotbar key, given the position. -1 if no key are pressed */ - public int handleGuiBar(int x, int y) { - if (!CallbackBridge.isGrabbing()) return -1; - - int barHeight = mcScale(20); - int barY = CallbackBridge.physicalHeight - barHeight; - if(y < barY) return -1; - - int barWidth = mcScale(180); - int barX = (CallbackBridge.physicalWidth / 2) - (barWidth / 2); - if(x < barX || x >= barX + barWidth) return -1; - - return HOTBAR_KEYS[(int) net.kdt.pojavlaunch.utils.MathUtils.map(x, barX, barX + barWidth, 0, 9)]; - } - - /** Return the size, given the UI scale size */ - private int mcScale(int input) { - return (int)((mGuiScale * input)/ mScaleFactor); } }