Kaydet (Commit) df433a70 authored tarafından Tomaž Vajngerl's avatar Tomaž Vajngerl

android: upgrade PanZoomController - add configurable zoom limits

Change-Id: I19815f58af4d060cffe515829a2a5472d32bf83c
üst 0455b3d4
......@@ -118,6 +118,7 @@ public class LibreOfficeMainActivity extends Activity {
}
mLayerController = new LayerController(this);
mLayerController.setAllowZoom(true);
mLayerClient = new GeckoLayerClient(this);
mLayerController.setLayerClient(mLayerClient);
mGeckoLayout.addView(mLayerController.getView(), 0);
......
......@@ -36,7 +36,7 @@ final class DisplayPortCalculator {
private static final String PREF_DISPLAYPORT_VB_DANGER_Y_INCR = "gfx.displayport.strategy_vb.danger_y_incr";
private static final String PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD = "gfx.displayport.strategy_pb.threshold";
private static DisplayPortStrategy sStrategy = new NoMarginStrategy(null);
private static DisplayPortStrategy sStrategy = new DynamicResolutionStrategy(null);
static DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
return sStrategy.calculate(metrics, (velocity == null ? ZERO_VELOCITY : velocity));
......
......@@ -172,7 +172,7 @@ public class GeckoLayerClient implements LayerView.Listener {
// Don't adjust page size when zooming unless zoom levels are
// approximately equal.
if (FloatUtils.fuzzyEquals(mLayerController.getZoomFactor(), mGeckoViewport.getZoomFactor())) {
mLayerController.setPageSize(mGeckoViewport.getPageSize());
mLayerController.setPageSize(mGeckoViewport.getPageSize(), mGeckoViewport.getPageSize());
}
} else {
mLayerController.setViewportMetrics(mGeckoViewport);
......@@ -254,7 +254,7 @@ public class GeckoLayerClient implements LayerView.Listener {
float ourZoom = mLayerController.getZoomFactor();
pageWidth = pageWidth * ourZoom / zoom;
pageHeight = pageHeight * ourZoom /zoom;
mLayerController.setPageSize(new FloatSize(pageWidth, pageHeight));
mLayerController.setPageSize(new FloatSize(pageWidth, pageHeight), new FloatSize(pageWidth, pageHeight));
// Here the page size of the document has changed, but the document being displayed
// is still the same. Therefore, we don't need to send anything to browser.js; any
// changes we need to make to the display port will get sent the next time we call
......@@ -296,13 +296,13 @@ public class GeckoLayerClient implements LayerView.Listener {
}
@Override
public void compositionResumeRequested() {
public void compositionResumeRequested(int width, int height) {
}
@Override
public void surfaceChanged(int width, int height) {
compositionResumeRequested();
compositionResumeRequested(width, height);
renderRequested();
}
......
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Android code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@mozilla.com>
* Chris Lord <chrislord.net@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.gfx;
......@@ -42,6 +9,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.RectF;
import android.view.GestureDetector;
......@@ -49,8 +17,6 @@ import android.view.GestureDetector;
import org.mozilla.gecko.ui.PanZoomController;
import org.mozilla.gecko.ui.SimpleScaleGestureDetector;
import java.util.regex.Pattern;
/**
* The layer controller manages a tile that represents the visible page. It does panning and
* zooming natively by delegating to a panning/zooming controller. Touch events can be dispatched
......@@ -87,12 +53,15 @@ public class LayerController {
private GeckoLayerClient mLayerClient; /* The layer client. */
/* The new color for the checkerboard. */
private int mCheckerboardColor;
private int mCheckerboardColor = Color.WHITE;
private boolean mCheckerboardShouldShowChecks;
private boolean mForceRedraw;
private boolean mAllowZoom;
private float mDefaultZoom;
private float mMinZoom;
private float mMaxZoom;
private static Pattern sColorPattern;
private boolean mForceRedraw;
public LayerController(Context context) {
mContext = context;
......@@ -124,6 +93,10 @@ public class LayerController {
return mViewportMetrics.getViewport();
}
public RectF getCssViewport() {
return mViewportMetrics.getCssViewport();
}
public FloatSize getViewportSize() {
return mViewportMetrics.getSize();
}
......@@ -132,6 +105,10 @@ public class LayerController {
return mViewportMetrics.getPageSize();
}
public FloatSize getCssPageSize() {
return mViewportMetrics.getCssPageSize();
}
public PointF getOrigin() {
return mViewportMetrics.getOrigin();
}
......@@ -189,12 +166,12 @@ public class LayerController {
}
/** Sets the current page size. You must hold the monitor while calling this. */
public void setPageSize(FloatSize size) {
if (mViewportMetrics.getPageSize().fuzzyEquals(size))
public void setPageSize(FloatSize size, FloatSize cssSize) {
if (mViewportMetrics.getCssPageSize().equals(cssSize))
return;
ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
viewportMetrics.setPageSize(size, size);
viewportMetrics.setPageSize(size, cssSize);
mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
// Page size is owned by the layer client, so no need to notify it of
......@@ -294,8 +271,9 @@ public class LayerController {
* correct.
*/
public PointF convertViewPointToLayerPoint(PointF viewPoint) {
if (mRootLayer == null)
if (mLayerClient == null) {
return null;
}
ImmutableViewportMetrics viewportMetrics = mViewportMetrics;
PointF origin = viewportMetrics.getOrigin();
......@@ -337,5 +315,41 @@ public class LayerController {
mCheckerboardColor = newColor;
mView.requestRender();
}
}
public void setAllowZoom(final boolean aValue) {
mAllowZoom = aValue;
mView.post(new Runnable() {
public void run() {
mView.getTouchEventHandler().setDoubleTapEnabled(aValue);
}
});
}
public boolean getAllowZoom() {
return mAllowZoom;
}
public void setDefaultZoom(float aValue) {
mDefaultZoom = aValue;
}
public float getDefaultZoom() {
return mDefaultZoom;
}
public void setMinZoom(float aValue) {
mMinZoom = aValue;
}
public float getMinZoom() {
return mMinZoom;
}
public void setMaxZoom(float aValue) {
mMaxZoom = aValue;
}
public float getMaxZoom() {
return mMaxZoom;
}
}
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Android code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@mozilla.com>
* Arkady Blyakher <rkadyb@mit.edu>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.gfx;
......@@ -42,7 +9,6 @@ package org.mozilla.gecko.gfx;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.opengl.GLSurfaceView;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
......@@ -227,7 +193,7 @@ public class LayerView extends SurfaceView implements SurfaceHolder.Callback {
}
public GLSurfaceView.Renderer getRenderer() {
public LayerRenderer getRenderer() {
return mRenderer;
}
......@@ -235,7 +201,7 @@ public class LayerView extends SurfaceView implements SurfaceHolder.Callback {
mListener = listener;
}
public synchronized GLController getGLController() {
public GLController getGLController() {
return mGLController;
}
......@@ -288,7 +254,7 @@ public class LayerView extends SurfaceView implements SurfaceHolder.Callback {
public interface Listener {
void renderRequested();
void compositionPauseRequested();
void compositionResumeRequested();
void compositionResumeRequested(int width, int height);
void surfaceChanged(int width, int height);
}
......
......@@ -10,8 +10,8 @@ import android.os.SystemClock;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import org.mozilla.gecko.ui.PanZoomController;
import org.mozilla.gecko.ui.SimpleScaleGestureDetector;
import java.util.LinkedList;
......@@ -41,18 +41,24 @@ import java.util.Queue;
* at some point after the first or second event in the block is processed in Gecko.
* This code assumes we get EXACTLY ONE default-prevented notification for each block
* of events.
*
* Note that even if all events are default-prevented, we still send specific types
* of notifications to the pan/zoom controller. The notifications are needed
* to respond to user actions a timely manner regardless of default-prevention,
* and fix issues like bug 749384.
*/
public final class TouchEventHandler {
private static final String LOGTAG = "GeckoTouchEventHandler";
// The time limit for listeners to respond with preventDefault on touchevents
// before we begin panning the page
private final int EVENT_LISTENER_TIMEOUT = ViewConfiguration.getLongPressTimeout();
private final int EVENT_LISTENER_TIMEOUT = 200;
private final LayerView mView;
private final LayerController mController;
private final GestureDetector mGestureDetector;
private final SimpleScaleGestureDetector mScaleGestureDetector;
private final PanZoomController mPanZoomController;
private final GestureDetector.OnDoubleTapListener mDoubleTapListener;
// the queue of events that we are holding on to while waiting for a preventDefault
// notification
......@@ -119,15 +125,16 @@ public final class TouchEventHandler {
TouchEventHandler(Context context, LayerView view, LayerController controller) {
mView = view;
mController = controller;
mEventQueue = new LinkedList<MotionEvent>();
mGestureDetector = new GestureDetector(context, controller.getGestureListener());
mScaleGestureDetector = new SimpleScaleGestureDetector(controller.getScaleGestureListener());
mPanZoomController = controller.getPanZoomController();
mListenerTimeoutProcessor = new ListenerTimeoutProcessor();
mDispatchEvents = true;
mGestureDetector.setOnDoubleTapListener(controller.getDoubleTapListener());
mDoubleTapListener = controller.getDoubleTapListener();
setDoubleTapEnabled(true);
}
/* This function MUST be called on the UI thread */
......@@ -142,7 +149,18 @@ public final class TouchEventHandler {
if (isDownEvent(event)) {
// this is the start of a new block of events! whee!
mHoldInQueue = mWaitForTouchListeners;
// Set mDispatchEvents to true so that we are guaranteed to either queue these
// events or dispatch them. The only time we should not do either is once we've
// heard back from content to preventDefault this block.
mDispatchEvents = true;
if (mHoldInQueue) {
// if the new block we are starting is the current block (i.e. there are no
// other blocks waiting in the queue, then we should let the pan/zoom controller
// know we are waiting for the touch listeners to run
if (mEventQueue.isEmpty()) {
mPanZoomController.waitingForTouchListeners(event);
}
// if we're holding the events in the queue, set the timeout so that
// we dispatch these events if we don't get a default-prevented notification
mView.postDelayed(mListenerTimeoutProcessor, EVENT_LISTENER_TIMEOUT);
......@@ -164,6 +182,8 @@ public final class TouchEventHandler {
mEventQueue.add(MotionEvent.obtain(event));
} else if (mDispatchEvents) {
dispatchEvent(event);
} else if (touchFinished(event)) {
mPanZoomController.preventedTouchFinished();
}
// notify gecko of the event
......@@ -192,6 +212,11 @@ public final class TouchEventHandler {
mProcessingBalance--;
}
/* This function MUST be called on the UI thread. */
public void setDoubleTapEnabled(boolean aValue) {
mGestureDetector.setOnDoubleTapListener(aValue ? mDoubleTapListener : null);
}
/* This function MUST be called on the UI thread. */
public void setWaitForTouchListeners(boolean aValue) {
mWaitForTouchListeners = aValue;
......@@ -207,18 +232,32 @@ public final class TouchEventHandler {
return (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN);
}
private boolean touchFinished(MotionEvent event) {
int action = (event.getAction() & MotionEvent.ACTION_MASK);
return (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL);
}
/**
* Dispatch the event to the gesture detectors and the pan/zoom controller.
*/
private void dispatchEvent(MotionEvent event) {
if (mGestureDetector.onTouchEvent(event)) {
return;
// An up/cancel event should get passed to both detectors, in
// case it comes from a pointer the scale detector is tracking.
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
default:
return;
}
}
mScaleGestureDetector.onTouchEvent(event);
if (mScaleGestureDetector.isInProgress()) {
return;
}
mController.getPanZoomController().onTouchEvent(event);
mPanZoomController.onTouchEvent(event);
}
/**
......@@ -244,6 +283,8 @@ public final class TouchEventHandler {
// default-prevented.
if (allowDefaultAction) {
dispatchEvent(event);
} else if (touchFinished(event)) {
mPanZoomController.preventedTouchFinished();
}
event = mEventQueue.peek();
if (event == null) {
......@@ -259,6 +300,7 @@ public final class TouchEventHandler {
if (isDownEvent(event)) {
// we have finished processing the block we were interested in.
// now we wait for the next call to processEventBlock
mPanZoomController.waitingForTouchListeners(event);
break;
}
// pop the event we peeked above, as it is still part of the block and
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment