Auto merge of #21431 - paulrouget:pinchtozoom, r=jdm

Pinch to zoom

Implement pinch to zoom.

Also, I added a marker in the toolbar to show if Servo is redrawing or not.

The label switches from "IDLE" to "LOOP" when Servo is looping (at 60FPS) to redraw the page.

Redrawing is necessary for 3 reasons:
- a CSS animation is ongoing
- scrolling
- zooming

This label is, for now, necessary to make sure we are not looping for not reason.

---
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #21303 (github issue number if applicable).

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21431)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-08-16 12:29:50 -04:00 committed by GitHub
commit 14fff5bbe0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 194 additions and 19 deletions

View file

@ -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 =

View file

@ -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");

View file

@ -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");

View file

@ -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();

View file

@ -82,6 +82,17 @@
android:text="Rld"
android:textSize="10sp" />
<TextView
android:id="@+id/redrawing"
android:layout_width="70dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fontFamily="monospace"
android:text="idle"
android:textAlignment="center"
android:textSize="10sp"
android:visibility="visible" />
</LinearLayout>
<com.mozilla.servoview.ServoView

View file

@ -49,6 +49,12 @@ public class JNIServo {
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 {

View file

@ -86,6 +86,18 @@ public class Servo {
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));
}
@ -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);

View file

@ -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();