Skip to content

Commit

Permalink
Fix[input]: make hotbar behave more like an android button, move hotb…
Browse files Browse the repository at this point in the history
…ar handling into a separate class
  • Loading branch information
artdeell committed Apr 6, 2024
1 parent e5c0555 commit 644f39b
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 79 deletions.
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -1,75 +1,55 @@
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;
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mGuiBarTracker.cancel();
mTracker.cancelTracking();
cancelGestures(false);
}
Expand All @@ -81,56 +61,13 @@ public void cancelPendingActions() {
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);
}
}

0 comments on commit 644f39b

Please sign in to comment.