diff --git a/ports/libsimpleservo/src/api.rs b/ports/libsimpleservo/src/api.rs index cdaa4a67cf2e..4e904d698f1d 100644 --- a/ports/libsimpleservo/src/api.rs +++ b/ports/libsimpleservo/src/api.rs @@ -286,6 +286,24 @@ impl ServoGlue { self.process_event(event) } + /// Start pinchzoom. + /// x/y are pinch origin coordinates. + pub fn pinchzoom_start(&mut self, factor: f32, _x: u32, _y: u32) -> Result<(), &'static str> { + self.process_event(WindowEvent::PinchZoom(factor)) + } + + /// Pinchzoom. + /// x/y are pinch origin coordinates. + pub fn pinchzoom(&mut self, factor: f32, _x: u32, _y: u32) -> Result<(), &'static str> { + self.process_event(WindowEvent::PinchZoom(factor)) + } + + /// End pinchzoom. + /// x/y are pinch origin coordinates. + pub fn pinchzoom_end(&mut self, factor: f32, _x: u32, _y: u32) -> Result<(), &'static str> { + self.process_event(WindowEvent::PinchZoom(factor)) + } + /// Perform a click. pub fn click(&mut self, x: u32, y: u32) -> Result<(), &'static str> { let mouse_event = diff --git a/ports/libsimpleservo/src/capi.rs b/ports/libsimpleservo/src/capi.rs index 3edadc81dd52..dafa49373ef2 100644 --- a/ports/libsimpleservo/src/capi.rs +++ b/ports/libsimpleservo/src/capi.rs @@ -180,6 +180,24 @@ pub extern "C" fn scroll(dx: i32, dy: i32, x: i32, y: i32) { call(|s| s.scroll(dx as i32, dy as i32, x as u32, y as u32)); } +#[no_mangle] +pub extern "C" fn pinchzoom_start(factor: f32, x: i32, y: i32) { + debug!("pinchzoom_start"); + call(|s| s.pinchzoom_start(factor, x as u32, y as u32)); +} + +#[no_mangle] +pub extern "C" fn pinchzoom(factor: f32, x: i32, y: i32) { + debug!("pinchzoom"); + call(|s| s.pinchzoom(factor, x as u32, y as u32)); +} + +#[no_mangle] +pub extern "C" fn pinchzoom_end(factor: f32, x: i32, y: i32) { + debug!("pinchzoom_end"); + call(|s| s.pinchzoom_end(factor, x as u32, y as u32)); +} + #[no_mangle] pub extern "C" fn click(x: i32, y: i32) { debug!("click"); diff --git a/ports/libsimpleservo/src/jniapi.rs b/ports/libsimpleservo/src/jniapi.rs index 2367b685d479..26817bd45614 100644 --- a/ports/libsimpleservo/src/jniapi.rs +++ b/ports/libsimpleservo/src/jniapi.rs @@ -9,7 +9,7 @@ use api::{self, EventLoopWaker, ServoGlue, SERVO, HostTrait, ReadFileTrait}; use gl_glue; use jni::{JNIEnv, JavaVM}; use jni::objects::{GlobalRef, JClass, JObject, JString, JValue}; -use jni::sys::{jboolean, jint, jstring, JNI_TRUE}; +use jni::sys::{jboolean, jfloat, jint, jstring, JNI_TRUE}; use log::Level; use std; use std::os::raw::c_void; @@ -207,6 +207,43 @@ pub fn Java_com_mozilla_servoview_JNIServo_scroll( call(env, |s| s.scroll(dx as i32, dy as i32, x as u32, y as u32)); } +#[no_mangle] +pub fn Java_com_mozilla_servoview_JNIServo_pinchZoomStart( + env: JNIEnv, + _: JClass, + factor: jfloat, + x: jint, + y: jint, +) { + debug!("pinchZoomStart"); + call(env, |s| s.pinchzoom_start(factor as f32, x as u32, y as u32)); +} + +#[no_mangle] +pub fn Java_com_mozilla_servoview_JNIServo_pinchZoom( + env: JNIEnv, + _: JClass, + factor: jfloat, + x: jint, + y: jint, +) { + debug!("pinchZoom"); + call(env, |s| s.pinchzoom(factor as f32, x as u32, y as u32)); +} + +#[no_mangle] +pub fn Java_com_mozilla_servoview_JNIServo_pinchZoomEnd( + env: JNIEnv, + _: JClass, + factor: jfloat, + x: jint, + y: jint, +) { + debug!("pinchZoomEnd"); + call(env, |s| s.pinchzoom_end(factor as f32, x as u32, y as u32)); +} + + #[no_mangle] pub fn Java_com_mozilla_servoview_JNIServo_click(env: JNIEnv, _: JClass, x: jint, y: jint) { debug!("click"); diff --git a/support/android/apk/servoview/src/main/java/com/mozilla/servoview/JNIServo.java b/support/android/apk/servoview/src/main/java/com/mozilla/servoview/JNIServo.java index aa8125038fa8..a740f474af12 100644 --- a/support/android/apk/servoview/src/main/java/com/mozilla/servoview/JNIServo.java +++ b/support/android/apk/servoview/src/main/java/com/mozilla/servoview/JNIServo.java @@ -49,6 +49,12 @@ public native void init(Activity activity, public native void scrollEnd(int dx, int dy, int x, int y); + public native void pinchZoomStart(float factor, int x, int y); + + public native void pinchZoom(float factor, int x, int y); + + public native void pinchZoomEnd(float factor, int x, int y); + public native void click(int x, int y); public interface Callbacks { diff --git a/support/android/apk/servoview/src/main/java/com/mozilla/servoview/Servo.java b/support/android/apk/servoview/src/main/java/com/mozilla/servoview/Servo.java index e125d245d75b..d517aaf14385 100644 --- a/support/android/apk/servoview/src/main/java/com/mozilla/servoview/Servo.java +++ b/support/android/apk/servoview/src/main/java/com/mozilla/servoview/Servo.java @@ -86,6 +86,18 @@ public void scrollEnd(int dx, int dy, int x, int y) { mRunCallback.inGLThread(() -> mJNI.scrollEnd(dx, dy, x, y)); } + public void pinchZoomStart(float factor, int x, int y) { + mRunCallback.inGLThread(() -> mJNI.pinchZoomStart(factor, x, y)); + } + + public void pinchZoom(float factor, int x, int y) { + mRunCallback.inGLThread(() -> mJNI.pinchZoom(factor, x, y)); + } + + public void pinchZoomEnd(float factor, int x, int y) { + mRunCallback.inGLThread(() -> mJNI.pinchZoomEnd(factor, x, y)); + } + public void click(int x, int y) { mRunCallback.inGLThread(() -> mJNI.click(x, y)); } diff --git a/support/android/apk/servoview/src/main/java/com/mozilla/servoview/ServoView.java b/support/android/apk/servoview/src/main/java/com/mozilla/servoview/ServoView.java index 0424d84ebe08..ffc0c4504b7e 100644 --- a/support/android/apk/servoview/src/main/java/com/mozilla/servoview/ServoView.java +++ b/support/android/apk/servoview/src/main/java/com/mozilla/servoview/ServoView.java @@ -11,9 +11,11 @@ import android.opengl.GLES31; import android.opengl.GLSurfaceView; import android.util.AttributeSet; +import android.util.Log; import android.view.Choreographer; import android.view.GestureDetector; import android.view.MotionEvent; +import android.view.ScaleGestureDetector; import android.widget.OverScroller; import com.mozilla.servoview.Servo.Client; @@ -26,6 +28,7 @@ public class ServoView extends GLSurfaceView implements GestureDetector.OnGestureListener, + ScaleGestureDetector.OnScaleGestureListener, Choreographer.FrameCallback, GfxCallbacks, RunCallback { @@ -39,12 +42,18 @@ public class ServoView extends GLSurfaceView private boolean mAnimating; private String mServoArgs = ""; private GestureDetector mGestureDetector; + private ScaleGestureDetector mScaleGestureDetector; + private OverScroller mScroller; private int mLastX = 0; private int mCurX = 0; private int mLastY = 0; private int mCurY = 0; private boolean mFlinging; + private boolean mScrolling; + + private boolean mZooming; + private float mZoomFactor = 1; public ServoView(Context context, AttributeSet attrs) { super(context, attrs); @@ -138,19 +147,18 @@ public void setClient(Client client) { private void initGestures(Context context) { mGestureDetector = new GestureDetector(context, this); + mScaleGestureDetector = new ScaleGestureDetector(context, this); mScroller = new OverScroller(context); } public void doFrame(long frameTimeNanos) { - if (mScroller.isFinished() && mFlinging) { + // 3 reasons to be here: animating or scrolling/flinging or pinching + + if (mFlinging && mScroller.isFinished()) { mFlinging = false; - inGLThread(() -> mServo.scrollEnd(0, 0, mCurX, mCurY)); - if (!mAnimating) { - // Not scrolling. Not animating. We don't need to schedule - // another frame. - return; - } + mScrolling = false; + mServo.scrollEnd(0, 0, mCurX, mCurY); } if (mFlinging) { @@ -165,15 +173,25 @@ public void doFrame(long frameTimeNanos) { mLastX = mCurX; mLastY = mCurY; - if (dx != 0 || dy != 0) { - inGLThread(() -> mServo.scroll(dx, dy, mCurX, mCurY)); - } else { - if (mAnimating) { - requestRender(); - } + boolean scrollNecessary = mScrolling && (dx != 0 || dy != 0); + boolean zoomNecessary = mZooming && mZoomFactor != 1; + + if (scrollNecessary) { + mServo.scroll(dx, dy, mCurX, mCurY); + } + + if (zoomNecessary) { + mServo.pinchZoom(mZoomFactor, 0, 0); + mZoomFactor = 1; + } + + if (!zoomNecessary && !scrollNecessary && mAnimating) { + requestRender(); } - Choreographer.getInstance().postFrameCallback(this); + if (mZooming || mScrolling || mAnimating) { + Choreographer.getInstance().postFrameCallback(this); + } } public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { @@ -198,6 +216,7 @@ public boolean onDown(MotionEvent e) { public boolean onTouchEvent(final MotionEvent e) { mGestureDetector.onTouchEvent(e); + mScaleGestureDetector.onTouchEvent(e); int action = e.getActionMasked(); switch (action) { @@ -207,7 +226,8 @@ public boolean onTouchEvent(final MotionEvent e) { mCurY = (int) e.getY(); mLastY = mCurY; mScroller.forceFinished(true); - inGLThread(() -> mServo.scrollStart(0, 0, mCurX, mCurY)); + mServo.scrollStart(0, 0, mCurX, mCurY); + mScrolling = true; Choreographer.getInstance().postFrameCallback(this); return true; case (MotionEvent.ACTION_MOVE): @@ -217,8 +237,8 @@ public boolean onTouchEvent(final MotionEvent e) { case (MotionEvent.ACTION_UP): case (MotionEvent.ACTION_CANCEL): if (!mFlinging) { - inGLThread(() -> mServo.scrollEnd(0, 0, mCurX, mCurY)); - Choreographer.getInstance().removeFrameCallback(this); + mScrolling = false; + mServo.scrollEnd(0, 0, mCurX, mCurY); } return true; default: @@ -227,7 +247,7 @@ public boolean onTouchEvent(final MotionEvent e) { } public boolean onSingleTapUp(MotionEvent e) { - inGLThread(() -> mServo.click((int) e.getX(), (int) e.getY())); + mServo.click((int) e.getX(), (int) e.getY()); return false; } @@ -241,6 +261,33 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d public void onShowPress(MotionEvent e) { } + @Override + public boolean onScaleBegin(ScaleGestureDetector detector) { + if (mScroller.isFinished()) { + mZoomFactor = detector.getScaleFactor(); + mZooming = true; + mServo.pinchZoomStart(mZoomFactor, 0, 0); + Choreographer.getInstance().postFrameCallback(this); + return true; + } else { + return false; + } + } + + @Override + public boolean onScale(ScaleGestureDetector detector) { + mZoomFactor *= detector.getScaleFactor(); + return true; + } + + @Override + public void onScaleEnd(ScaleGestureDetector detector) { + mZoomFactor = detector.getScaleFactor(); + mZooming = false; + mServo.pinchZoomEnd(mZoomFactor, 0, 0); + } + + @Override public void onPause() { super.onPause();