Fix[input]: implement scrolling gestures for touchpads

This commit is contained in:
artdeell
2024-05-01 13:37:48 +03:00
committed by Maksim Belov
parent 754bdf3047
commit 7614989b09
3 changed files with 67 additions and 23 deletions

View File

@@ -15,10 +15,12 @@ import org.lwjgl.glfw.CallbackBridge;
@RequiresApi(api = Build.VERSION_CODES.O)
public class AndroidPointerCapture implements ViewTreeObserver.OnWindowFocusChangeListener, View.OnCapturedPointerListener {
private static final float TOUCHPAD_SCROLL_THRESHOLD = 1;
private final AbstractTouchpad mTouchpad;
private final View mHostView;
private final float mScaleFactor;
private final float mPointerPrescale = Tools.dpToPx(1);
private final float mMousePrescale = Tools.dpToPx(1);
private final Scroller mScroller = new Scroller(TOUCHPAD_SCROLL_THRESHOLD);
public AndroidPointerCapture(AbstractTouchpad touchpad, View hostView, float scaleFactor) {
this.mScaleFactor = scaleFactor;
@@ -47,13 +49,26 @@ public class AndroidPointerCapture implements ViewTreeObserver.OnWindowFocusChan
@Override
public boolean onCapturedPointer(View view, MotionEvent event) {
// Yes, we actually not only receive relative mouse events here, but also absolute touchpad ones!
// Read from relative axis directly to work around.
float relX = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
float relY = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
if(!CallbackBridge.isGrabbing()) {
enableTouchpadIfNecessary();
mTouchpad.applyMotionVector(event.getX() * mPointerPrescale, event.getY() * mPointerPrescale);
// Yes, if the user's touchpad is multi-touch we will also receive events for that.
// So, handle the scrolling gesture ourselves.
relX *= mMousePrescale;
relY *= mMousePrescale;
if(event.getPointerCount() < 2) {
mTouchpad.applyMotionVector(relX, relY);
mScroller.resetScrollOvershoot();
} else {
mScroller.performScroll(relX, relY);
}
} else {
// Position is updated by many events, hence it is send regardless of the event value
CallbackBridge.mouseX += (event.getX() * mScaleFactor);
CallbackBridge.mouseY += (event.getY() * mScaleFactor);
CallbackBridge.mouseX += (relX * mScaleFactor);
CallbackBridge.mouseY += (relY * mScaleFactor);
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
}

View File

@@ -11,13 +11,13 @@ import net.kdt.pojavlaunch.prefs.LauncherPreferences;
import org.lwjgl.glfw.CallbackBridge;
public class InGUIEventProcessor implements TouchEventProcessor {
public static final float FINGER_SCROLL_THRESHOLD = Tools.dpToPx(6);
private final PointerTracker mTracker = new PointerTracker();
private final GestureDetector mSingleTapDetector;
private AbstractTouchpad mTouchpad;
private boolean mIsMouseDown = false;
private final float mScaleFactor;
private float mScrollOvershootH, mScrollOvershootV;
public static final float FINGER_SCROLL_THRESHOLD = Tools.dpToPx(6);
private final Scroller mScroller = new Scroller(FINGER_SCROLL_THRESHOLD);
public InGUIEventProcessor(float scaleFactor) {
mSingleTapDetector = new GestureDetector(null, new SingleTapConfirm());
mScaleFactor = scaleFactor;
@@ -44,11 +44,11 @@ public class InGUIEventProcessor implements TouchEventProcessor {
sendTouchCoordinates(mainPointerX, mainPointerY);
if(!mIsMouseDown) enableMouse();
}
} else performScroll();
} else mScroller.performScroll(mTracker.getMotionVector());
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
resetScrollOvershoot();
mScroller.resetScrollOvershoot();
mTracker.cancelTracking();
if(mIsMouseDown) disableMouse();
}
@@ -64,20 +64,6 @@ public class InGUIEventProcessor implements TouchEventProcessor {
mTouchpad = touchpad;
}
private void performScroll() {
float[] motionVector = mTracker.getMotionVector();
float hScroll = (motionVector[0] / FINGER_SCROLL_THRESHOLD) + mScrollOvershootH;
float vScroll = (motionVector[1] / FINGER_SCROLL_THRESHOLD) + mScrollOvershootV;
int hScrollRound = (int) hScroll, vScrollRound = (int) vScroll;
if(hScrollRound != 0 || vScrollRound != 0) CallbackBridge.sendScroll(hScroll, vScroll);
mScrollOvershootH = hScroll - hScrollRound;
mScrollOvershootV = vScroll - vScrollRound;
}
private void resetScrollOvershoot() {
mScrollOvershootH = mScrollOvershootV = 0f;
}
private void sendTouchCoordinates(float x, float y) {
CallbackBridge.sendCursorPos( x * mScaleFactor, y * mScaleFactor);
}
@@ -99,7 +85,7 @@ public class InGUIEventProcessor implements TouchEventProcessor {
@Override
public void cancelPendingActions() {
resetScrollOvershoot();
mScroller.resetScrollOvershoot();
disableMouse();
}
}

View File

@@ -0,0 +1,43 @@
package net.kdt.pojavlaunch.customcontrols.mouse;
import org.lwjgl.glfw.CallbackBridge;
public class Scroller {
private float mScrollOvershootH, mScrollOvershootV;
private final float mScrollThreshold;
public Scroller(float mScrollThreshold) {
this.mScrollThreshold = mScrollThreshold;
}
/**
* Perform a scrolling gesture.
* @param dx the X coordinate of the primary pointer's vector
* @param dy the Y coordinate of the primary pointer's vector
*/
public void performScroll(float dx, float dy) {
float hScroll = (dx / mScrollThreshold) + mScrollOvershootH;
float vScroll = (dy / mScrollThreshold) + mScrollOvershootV;
int hScrollRound = (int) hScroll, vScrollRound = (int) vScroll;
if(hScrollRound != 0 || vScrollRound != 0) CallbackBridge.sendScroll(hScroll, vScroll);
mScrollOvershootH = hScroll - hScrollRound;
mScrollOvershootV = vScroll - vScrollRound;
}
/**
* Perform a scrolling gesture.
* @param vector a 2-component vector that stores the relative position of the primary pointer.
*/
public void performScroll(float[] vector) {
performScroll(vector[0], vector[1]);
}
/**
* Reset scroll overshoot values. Scroll overshoot makes the scrolling feel less
* choppy, but will cause anomailes if not reset on the end of a scrolling gesture.
*/
public void resetScrollOvershoot() {
mScrollOvershootH = mScrollOvershootV = 0f;
}
}