From 6bac7ee10b2f1e0774c749ff1aed0663f912b60f Mon Sep 17 00:00:00 2001 From: artdeell Date: Sun, 7 Apr 2024 15:25:13 -0400 Subject: [PATCH] Feat[input]: Minecraft hotbar as a View --- .../kdt/pojavlaunch/MinecraftGLSurface.java | 2 +- .../customcontrols/mouse/DropGesture.java | 5 +- .../customcontrols/mouse/HotbarTracker.java | 114 ------------- .../customcontrols/mouse/HotbarView.java | 150 ++++++++++++++++++ .../mouse/InGameEventProcessor.java | 18 +-- .../customcontrols/mouse/PointerTracker.java | 2 +- .../src/main/res/layout/activity_basemain.xml | 3 + 7 files changed, 160 insertions(+), 134 deletions(-) delete mode 100644 app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/HotbarTracker.java create mode 100644 app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/HotbarView.java diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java index 5e7b26159..92a702a3f 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java @@ -75,7 +75,7 @@ public class MinecraftGLSurface extends View implements GrabListener { /* View holding the surface, either a SurfaceView or a TextureView */ View mSurface; - private final TouchEventProcessor mIngameProcessor = new InGameEventProcessor(mScaleFactor, mSensitivityFactor); + private final TouchEventProcessor mIngameProcessor = new InGameEventProcessor(mSensitivityFactor); private final TouchEventProcessor mInGUIProcessor = new InGUIEventProcessor(mScaleFactor); private boolean mLastGrabState = false; diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/DropGesture.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/DropGesture.java index f51ef6b44..b0eb6817c 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/DropGesture.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/DropGesture.java @@ -15,12 +15,11 @@ public class DropGesture implements Runnable{ this.mHandler = mHandler; } - public void submit(boolean hasGuiBarHit) { - if(hasGuiBarHit && !mActive) { + public void submit() { + if(!mActive) { mActive = true; mHandler.postDelayed(this, LauncherPreferences.PREF_LONGPRESS_TRIGGER); } - if(!hasGuiBarHit && mActive) cancel(); } public void cancel() { 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 deleted file mode 100644 index 1aa84a91e..000000000 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/HotbarTracker.java +++ /dev/null @@ -1,114 +0,0 @@ -package net.kdt.pojavlaunch.customcontrols.mouse; - -import static net.kdt.pojavlaunch.utils.MCOptionUtils.getMcScale; - -import android.os.Handler; -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; - -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, boolean hasDoubleTapped) { - 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, hasDoubleTapped); - 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, hasDoubleTapped); - mDropGesture.submit(true); - }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, boolean hasDoubleTapped) { - if(hasDoubleTapped && !LauncherPreferences.PREF_DISABLE_SWAP_HAND) { - CallbackBridge.sendKeyPress(LwjglGlfwKeycode.GLFW_KEY_F); - } - - 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/HotbarView.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/HotbarView.java new file mode 100644 index 000000000..a0a723676 --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/HotbarView.java @@ -0,0 +1,150 @@ +package net.kdt.pojavlaunch.customcontrols.mouse; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; + +import androidx.annotation.Nullable; + +import net.kdt.pojavlaunch.LwjglGlfwKeycode; +import net.kdt.pojavlaunch.TapDetector; +import net.kdt.pojavlaunch.Tools; +import net.kdt.pojavlaunch.prefs.LauncherPreferences; +import net.kdt.pojavlaunch.utils.MCOptionUtils; +import net.kdt.pojavlaunch.utils.MathUtils; + +import org.lwjgl.glfw.CallbackBridge; + +public class HotbarView extends View implements MCOptionUtils.MCOptionListener, View.OnLayoutChangeListener, Runnable { + private final TapDetector mDoubleTapDetector = new TapDetector(2, TapDetector.DETECTION_METHOD_DOWN); + 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 final DropGesture mDropGesture = new DropGesture(new Handler(Looper.getMainLooper())); + private final float mScaleFactor = LauncherPreferences.PREF_SCALE_FACTOR/100f; + private int mWidth; + private int mLastIndex; + private int mGuiScale; + + public HotbarView(Context context) { + super(context); + initialize(); + } + + public HotbarView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + initialize(); + } + + public HotbarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initialize(); + } + + @SuppressWarnings("unused") // You suggested me this constructor, Android + public HotbarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + initialize(); + } + + private void initialize() { + MCOptionUtils.addMCOptionListener(this); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + ViewParent parent = getParent(); + if(parent == null) return; + if(parent instanceof View) { + View parentView = (View) parent; + parentView.addOnLayoutChangeListener(this); + } + mGuiScale = MCOptionUtils.getMcScale(); + repositionView(); + } + + private void repositionView() { + ViewGroup.LayoutParams layoutParams = getLayoutParams(); + if(!(layoutParams instanceof ViewGroup.MarginLayoutParams)) + throw new RuntimeException("Incorrect LayoutParams type, expected ViewGroup.MarginLayoutParams"); + ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) layoutParams; + int height; + marginLayoutParams.width = mWidth = mcScale(180); + marginLayoutParams.height = height = mcScale(20); + marginLayoutParams.leftMargin = (CallbackBridge.physicalWidth / 2) - (mWidth / 2); + marginLayoutParams.topMargin = CallbackBridge.physicalHeight - height; + setLayoutParams(marginLayoutParams); + } + + @SuppressWarnings("ClickableViewAccessibility") // performClick does not report coordinates. + @Override + public boolean onTouchEvent(MotionEvent event) { + if(!CallbackBridge.isGrabbing()) return false; + // Check if we are double-tapping to swap hands + boolean hasDoubleTapped = mDoubleTapDetector.onTouchEvent(event); + if(hasDoubleTapped && !LauncherPreferences.PREF_DISABLE_SWAP_HAND) CallbackBridge.sendKeyPress(LwjglGlfwKeycode.GLFW_KEY_F); + + // Check if we need to cancel the drop event + int actionMasked = event.getActionMasked(); + if(isLastEventInGesture(actionMasked)) cancelDropGesture(); + else submitDropGesture(); + // Determine the hotbar slot + float x = event.getX(); + if(x < 0 || x > mWidth) return true; + int hotbarIndex = (int)MathUtils.map(x, 0, mWidth, 0, HOTBAR_KEYS.length); + // Check if the slot changed and we need to make a key press + if(hotbarIndex == mLastIndex) return true; + mLastIndex = hotbarIndex; + int hotbarKey = HOTBAR_KEYS[hotbarIndex]; + CallbackBridge.sendKeyPress(hotbarKey); + // Cancel the event since we changed hotbar slots. + cancelDropGesture(); + // Only resubmit the gesture only if it isn't the last event we will receive. + if(!isLastEventInGesture(actionMasked)) submitDropGesture(); + return true; + } + + private void submitDropGesture() { + if(LauncherPreferences.PREF_DISABLE_GESTURES) return; + mDropGesture.submit(); + } + + private void cancelDropGesture() { + if(LauncherPreferences.PREF_DISABLE_GESTURES) return; + mDropGesture.cancel(); + } + + private boolean isLastEventInGesture(int actionMasked) { + return actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL; + } + + private int mcScale(int input) { + return (int)((mGuiScale * input)/ mScaleFactor); + } + + @Override + public void onOptionChanged() { + Tools.runOnUiThread(this); + } + + @Override + public void run() { + if(getParent() == null) return; + int scale = MCOptionUtils.getMcScale(); + if(scale == mGuiScale) return; + mGuiScale = scale; + repositionView(); + } + + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { + repositionView(); + } +} 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 7a8ebe681..d1d1054be 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 @@ -4,7 +4,6 @@ import android.os.Handler; import android.os.Looper; import android.view.MotionEvent; -import net.kdt.pojavlaunch.TapDetector; import net.kdt.pojavlaunch.prefs.LauncherPreferences; import org.lwjgl.glfw.CallbackBridge; @@ -13,34 +12,24 @@ public class InGameEventProcessor implements TouchEventProcessor { private final Handler mGestureHandler = new Handler(Looper.getMainLooper()); 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 TapDetector mDoubleTapDetector = new TapDetector(2, TapDetector.DETECTION_METHOD_DOWN); - public InGameEventProcessor(float scaleFactor, double sensitivity) { - mGuiBarTracker = new HotbarTracker(mGestureHandler, scaleFactor); + public InGameEventProcessor(double sensitivity) { mSensitivity = sensitivity; } @Override public boolean processTouchEvent(MotionEvent motionEvent) { - boolean hasDoubleTapped = mDoubleTapDetector.onTouchEvent(motionEvent); switch (motionEvent.getActionMasked()) { case MotionEvent.ACTION_DOWN: - if(mGuiBarTracker.begin(motionEvent, hasDoubleTapped)) break; mTracker.startTracking(motionEvent); if(LauncherPreferences.PREF_DISABLE_GESTURES) break; checkGestures(); break; - case MotionEvent.ACTION_POINTER_DOWN: - mGuiBarTracker.begin(motionEvent, hasDoubleTapped); - break; case MotionEvent.ACTION_MOVE: - 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; + mTracker.trackEvent(motionEvent); + if(LauncherPreferences.PREF_DISABLE_GESTURES) break; checkGestures(); float[] motionVector = mTracker.getMotionVector(); CallbackBridge.mouseX += motionVector[0] * mSensitivity; @@ -49,7 +38,6 @@ public class InGameEventProcessor implements TouchEventProcessor { break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - mGuiBarTracker.cancel(); mTracker.cancelTracking(); cancelGestures(false); } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/PointerTracker.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/PointerTracker.java index f0dcc88e0..7ee8096df 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/PointerTracker.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/PointerTracker.java @@ -24,7 +24,7 @@ public class PointerTracker { public int trackEvent(MotionEvent motionEvent) { int trackedPointerIndex = motionEvent.findPointerIndex(mTrackedPointerId); int pointerCount = motionEvent.getPointerCount(); - if(trackedPointerIndex == -1 || mPointerCount != pointerCount || mColdStart) { + if(trackedPointerIndex == -1 || mPointerCount != pointerCount || mColdStart) { startTracking(motionEvent); trackedPointerIndex = 0; } diff --git a/app_pojavlauncher/src/main/res/layout/activity_basemain.xml b/app_pojavlauncher/src/main/res/layout/activity_basemain.xml index d92b2eb80..b1a28bf3d 100644 --- a/app_pojavlauncher/src/main/res/layout/activity_basemain.xml +++ b/app_pojavlauncher/src/main/res/layout/activity_basemain.xml @@ -51,6 +51,9 @@ android:layout_height="12dp" android:elevation="10dp" android:layout_gravity="center_horizontal"/> +