diff --git a/ports/libsimpleservo/src/api.rs b/ports/libsimpleservo/src/api.rs
index cdaa4a67cf2..4e904d698f1 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 3edadc81dd5..dafa49373ef 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 2367b685d47..26817bd4561 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/servoapp/src/main/java/com/mozilla/servo/MainActivity.java b/support/android/apk/servoapp/src/main/java/com/mozilla/servo/MainActivity.java
index 794e8ae5564..15a0d49e0c8 100644
--- a/support/android/apk/servoapp/src/main/java/com/mozilla/servo/MainActivity.java
+++ b/support/android/apk/servoapp/src/main/java/com/mozilla/servo/MainActivity.java
@@ -18,6 +18,7 @@ import android.webkit.URLUtil;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
+import android.widget.TextView;
import com.mozilla.servoview.ServoView;
import com.mozilla.servoview.Servo;
@@ -35,6 +36,7 @@ public class MainActivity extends Activity implements Servo.Client {
Button mStopButton;
EditText mUrlField;
ProgressBar mProgressBar;
+ TextView mIdleText;
@Override
@@ -49,6 +51,7 @@ public class MainActivity extends Activity implements Servo.Client {
mStopButton = findViewById(R.id.stopbutton);
mUrlField = findViewById(R.id.urlfield);
mProgressBar = findViewById(R.id.progressbar);
+ mIdleText = findViewById(R.id.redrawing);
mBackButton.setEnabled(false);
mFwdButton.setEnabled(false);
@@ -151,6 +154,14 @@ public class MainActivity extends Activity implements Servo.Client {
mFwdButton.setEnabled(canGoForward);
}
+ public void onRedrawing(boolean redrawing) {
+ if (redrawing) {
+ mIdleText.setText("LOOP");
+ } else {
+ mIdleText.setText("IDLE");
+ }
+ }
+
@Override
public void onPause() {
mServoView.onPause();
diff --git a/support/android/apk/servoapp/src/main/res/layout/activity_main.xml b/support/android/apk/servoapp/src/main/res/layout/activity_main.xml
index 2d8fab9c663..b8ae7fb94c3 100644
--- a/support/android/apk/servoapp/src/main/res/layout/activity_main.xml
+++ b/support/android/apk/servoapp/src/main/res/layout/activity_main.xml
@@ -82,6 +82,17 @@
android:text="Rld"
android:textSize="10sp" />
+
+
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));
}
@@ -104,6 +116,8 @@ public class Servo {
void onUrlChanged(String url);
void onHistoryChanged(boolean canGoBack, boolean canGoForward);
+
+ void onRedrawing(boolean redrawing);
}
public interface RunCallback {
@@ -170,6 +184,10 @@ public class Servo {
mRunCallback.inUIThread(() -> mClient.onHistoryChanged(canGoBack, canGoForward));
}
+ public void onRedrawing(boolean redrawing) {
+ mRunCallback.inUIThread(() -> mClient.onRedrawing(redrawing));
+ }
+
public byte[] readfile(String file) {
try {
InputStream stream = mAssetMgr.open(file);
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 0424d84ebe0..c886b44ea4a 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.net.Uri;
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 @@ import javax.microedition.khronos.opengles.GL10;
public class ServoView extends GLSurfaceView
implements
GestureDetector.OnGestureListener,
+ ScaleGestureDetector.OnScaleGestureListener,
Choreographer.FrameCallback,
GfxCallbacks,
RunCallback {
@@ -39,12 +42,20 @@ 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;
+
+ private boolean mRedrawing;
public ServoView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -138,19 +149,22 @@ public class ServoView extends GLSurfaceView
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 (!mRedrawing) {
+ mRedrawing = true;
+ mClient.onRedrawing(mRedrawing);
+ }
- 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 +179,28 @@ public class ServoView extends GLSurfaceView
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);
}
- Choreographer.getInstance().postFrameCallback(this);
+ if (zoomNecessary) {
+ mServo.pinchZoom(mZoomFactor, 0, 0);
+ mZoomFactor = 1;
+ }
+
+ if (!zoomNecessary && !scrollNecessary && mAnimating) {
+ requestRender();
+ }
+
+ if (mZooming || mScrolling || mAnimating) {
+ Choreographer.getInstance().postFrameCallback(this);
+ } else {
+ mRedrawing = false;
+ mClient.onRedrawing(mRedrawing);
+ }
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
@@ -198,6 +225,7 @@ public class ServoView extends GLSurfaceView
public boolean onTouchEvent(final MotionEvent e) {
mGestureDetector.onTouchEvent(e);
+ mScaleGestureDetector.onTouchEvent(e);
int action = e.getActionMasked();
switch (action) {
@@ -207,7 +235,8 @@ public class ServoView extends GLSurfaceView
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 +246,8 @@ public class ServoView extends GLSurfaceView
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 +256,7 @@ public class ServoView extends GLSurfaceView
}
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 +270,33 @@ public class ServoView extends GLSurfaceView
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();