2011-11-18 10:28:17 -08:00
|
|
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
2012-05-21 04:12:37 -07:00
|
|
|
* 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/. */
|
2011-11-18 10:28:17 -08:00
|
|
|
|
|
|
|
package org.mozilla.gecko.gfx;
|
|
|
|
|
|
|
|
import org.mozilla.gecko.gfx.Layer;
|
|
|
|
import org.mozilla.gecko.ui.PanZoomController;
|
2012-01-23 19:18:24 -08:00
|
|
|
import org.mozilla.gecko.ui.SimpleScaleGestureDetector;
|
2011-11-18 10:28:17 -08:00
|
|
|
import android.content.Context;
|
|
|
|
import android.content.res.Resources;
|
|
|
|
import android.graphics.Bitmap;
|
|
|
|
import android.graphics.BitmapFactory;
|
2012-02-28 16:17:58 -08:00
|
|
|
import android.graphics.Color;
|
2011-11-18 10:28:17 -08:00
|
|
|
import android.graphics.PointF;
|
|
|
|
import android.graphics.RectF;
|
|
|
|
import android.util.Log;
|
|
|
|
import android.view.GestureDetector;
|
|
|
|
import android.view.View.OnTouchListener;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
* to a higher-level view.
|
2011-12-13 14:43:08 -08:00
|
|
|
*
|
|
|
|
* Many methods require that the monitor be held, with a synchronized (controller) { ... } block.
|
2011-11-18 10:28:17 -08:00
|
|
|
*/
|
2012-04-07 00:09:26 -07:00
|
|
|
public class LayerController {
|
2011-11-15 13:41:19 -08:00
|
|
|
private static final String LOGTAG = "GeckoLayerController";
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
private Layer mRootLayer; /* The root layer. */
|
2012-02-08 07:46:26 -08:00
|
|
|
private LayerView mView; /* The main rendering view. */
|
2011-11-18 10:28:17 -08:00
|
|
|
private Context mContext; /* The current context. */
|
2012-03-02 11:31:27 -08:00
|
|
|
|
|
|
|
/* This is volatile so that we can read and write to it from different threads.
|
|
|
|
* We avoid synchronization to make getting the viewport metrics from
|
|
|
|
* the compositor as cheap as possible. The viewport is immutable so
|
|
|
|
* we don't need to worry about anyone mutating it while we're reading from it.
|
|
|
|
* Specifically:
|
|
|
|
* 1) reading mViewportMetrics from any thread is fine without synchronization
|
|
|
|
* 2) writing to mViewportMetrics requires synchronizing on the layer controller object
|
|
|
|
* 3) whenver reading multiple fields from mViewportMetrics without synchronization (i.e. in
|
|
|
|
* case 1 above) you should always frist grab a local copy of the reference, and then use
|
|
|
|
* that because mViewportMetrics might get reassigned in between reading the different
|
|
|
|
* fields. */
|
|
|
|
private volatile ImmutableViewportMetrics mViewportMetrics; /* The current viewport metrics. */
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
/*
|
|
|
|
* The panning and zooming controller, which interprets pan and zoom gestures for us and
|
|
|
|
* updates our visible rect appropriately.
|
|
|
|
*/
|
2012-04-07 00:09:26 -07:00
|
|
|
private PanZoomController mPanZoomController;
|
2011-11-18 10:28:17 -08:00
|
|
|
|
2012-02-17 06:56:47 -08:00
|
|
|
private GeckoLayerClient mLayerClient; /* The layer client. */
|
2012-01-23 20:10:24 -08:00
|
|
|
|
|
|
|
/* The new color for the checkerboard. */
|
2012-04-23 10:28:15 -07:00
|
|
|
private int mCheckerboardColor = Color.WHITE;
|
2012-03-12 13:20:19 -07:00
|
|
|
private boolean mCheckerboardShouldShowChecks;
|
2011-11-18 10:28:17 -08:00
|
|
|
|
2012-05-18 08:24:27 -07:00
|
|
|
private boolean mAllowZoom;
|
|
|
|
private float mDefaultZoom;
|
2012-05-18 08:24:27 -07:00
|
|
|
private float mMinZoom;
|
|
|
|
private float mMaxZoom;
|
2012-05-18 08:24:27 -07:00
|
|
|
|
2011-11-23 11:08:20 -08:00
|
|
|
private boolean mForceRedraw;
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
public LayerController(Context context) {
|
|
|
|
mContext = context;
|
|
|
|
|
2011-11-23 11:08:20 -08:00
|
|
|
mForceRedraw = true;
|
2012-03-02 11:31:27 -08:00
|
|
|
mViewportMetrics = new ImmutableViewportMetrics(new ViewportMetrics());
|
2011-11-18 10:28:17 -08:00
|
|
|
mPanZoomController = new PanZoomController(this);
|
|
|
|
mView = new LayerView(context, this);
|
2012-03-12 13:20:19 -07:00
|
|
|
mCheckerboardShouldShowChecks = true;
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public void setRoot(Layer layer) { mRootLayer = layer; }
|
|
|
|
|
2012-02-17 06:56:47 -08:00
|
|
|
public void setLayerClient(GeckoLayerClient layerClient) {
|
2011-11-18 10:28:17 -08:00
|
|
|
mLayerClient = layerClient;
|
|
|
|
layerClient.setLayerController(this);
|
|
|
|
}
|
|
|
|
|
2011-11-23 11:08:20 -08:00
|
|
|
public void setForceRedraw() {
|
|
|
|
mForceRedraw = true;
|
|
|
|
}
|
|
|
|
|
2011-11-23 11:07:29 -08:00
|
|
|
public Layer getRoot() { return mRootLayer; }
|
2012-02-08 07:46:26 -08:00
|
|
|
public LayerView getView() { return mView; }
|
2011-11-23 11:07:29 -08:00
|
|
|
public Context getContext() { return mContext; }
|
2012-03-02 11:31:27 -08:00
|
|
|
public ImmutableViewportMetrics getViewportMetrics() { return mViewportMetrics; }
|
2011-11-23 11:07:29 -08:00
|
|
|
|
2011-11-23 11:07:47 -08:00
|
|
|
public RectF getViewport() {
|
2011-11-23 11:07:29 -08:00
|
|
|
return mViewportMetrics.getViewport();
|
|
|
|
}
|
|
|
|
|
2012-04-14 10:18:10 -07:00
|
|
|
public RectF getCssViewport() {
|
|
|
|
return mViewportMetrics.getCssViewport();
|
|
|
|
}
|
|
|
|
|
2011-11-23 11:07:47 -08:00
|
|
|
public FloatSize getViewportSize() {
|
|
|
|
return mViewportMetrics.getSize();
|
2011-11-23 11:07:29 -08:00
|
|
|
}
|
|
|
|
|
2012-05-23 07:49:52 -07:00
|
|
|
public RectF getPageRect() {
|
|
|
|
return mViewportMetrics.getPageRect();
|
2011-11-23 11:07:29 -08:00
|
|
|
}
|
2011-11-18 10:28:17 -08:00
|
|
|
|
2012-05-23 07:49:52 -07:00
|
|
|
public RectF getCssPageRect() {
|
|
|
|
return mViewportMetrics.getCssPageRect();
|
2012-04-12 13:00:56 -07:00
|
|
|
}
|
|
|
|
|
2011-11-23 11:07:47 -08:00
|
|
|
public PointF getOrigin() {
|
|
|
|
return mViewportMetrics.getOrigin();
|
|
|
|
}
|
|
|
|
|
|
|
|
public float getZoomFactor() {
|
2012-03-02 11:31:27 -08:00
|
|
|
return mViewportMetrics.zoomFactor;
|
2011-11-23 11:07:47 -08:00
|
|
|
}
|
|
|
|
|
2012-01-20 06:27:09 -08:00
|
|
|
public Bitmap getBackgroundPattern() { return getDrawable("background"); }
|
2011-11-18 10:28:17 -08:00
|
|
|
public Bitmap getShadowPattern() { return getDrawable("shadow"); }
|
|
|
|
|
2012-01-24 16:31:33 -08:00
|
|
|
public PanZoomController getPanZoomController() { return mPanZoomController; }
|
2011-11-18 10:28:17 -08:00
|
|
|
public GestureDetector.OnGestureListener getGestureListener() { return mPanZoomController; }
|
2012-01-23 19:18:24 -08:00
|
|
|
public SimpleScaleGestureDetector.SimpleScaleGestureListener getScaleGestureListener() {
|
|
|
|
return mPanZoomController;
|
|
|
|
}
|
2011-11-15 13:41:19 -08:00
|
|
|
public GestureDetector.OnDoubleTapListener getDoubleTapListener() { return mPanZoomController; }
|
2011-11-18 10:28:17 -08:00
|
|
|
|
|
|
|
private Bitmap getDrawable(String name) {
|
|
|
|
Resources resources = mContext.getResources();
|
|
|
|
int resourceID = resources.getIdentifier(name, "drawable", mContext.getPackageName());
|
|
|
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
|
|
|
options.inScaled = false;
|
|
|
|
return BitmapFactory.decodeResource(mContext.getResources(), resourceID, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-12-13 14:43:08 -08:00
|
|
|
* The view calls this function to indicate that the viewport changed size. It must hold the
|
|
|
|
* monitor while calling it.
|
2011-11-18 10:28:17 -08:00
|
|
|
*
|
|
|
|
* TODO: Refactor this to use an interface. Expose that interface only to the view and not
|
|
|
|
* to the layer client. That way, the layer client won't be tempted to call this, which might
|
|
|
|
* result in an infinite loop.
|
|
|
|
*/
|
2011-11-23 11:07:47 -08:00
|
|
|
public void setViewportSize(FloatSize size) {
|
2012-03-02 11:31:27 -08:00
|
|
|
ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
|
|
|
|
viewportMetrics.setSize(size);
|
|
|
|
mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
|
2012-01-20 21:14:03 -08:00
|
|
|
|
2012-02-21 08:46:48 -08:00
|
|
|
if (mLayerClient != null) {
|
2011-12-03 19:59:27 -08:00
|
|
|
mLayerClient.viewportSizeChanged();
|
2012-02-21 08:46:48 -08:00
|
|
|
}
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|
|
|
|
|
2011-12-13 14:43:08 -08:00
|
|
|
/** Scrolls the viewport by the given offset. You must hold the monitor while calling this. */
|
2011-11-23 11:07:47 -08:00
|
|
|
public void scrollBy(PointF point) {
|
2012-03-02 11:31:27 -08:00
|
|
|
ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
|
|
|
|
PointF origin = viewportMetrics.getOrigin();
|
2011-11-23 11:07:47 -08:00
|
|
|
origin.offset(point.x, point.y);
|
2012-03-02 11:31:27 -08:00
|
|
|
viewportMetrics.setOrigin(origin);
|
|
|
|
mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
|
2011-11-18 10:28:17 -08:00
|
|
|
|
2011-11-23 11:07:29 -08:00
|
|
|
notifyLayerClientOfGeometryChange();
|
2011-11-30 09:27:13 -08:00
|
|
|
mView.requestRender();
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|
|
|
|
|
2012-05-23 07:49:52 -07:00
|
|
|
/** Sets the current page rect. You must hold the monitor while calling this. */
|
|
|
|
public void setPageRect(RectF rect, RectF cssRect) {
|
|
|
|
// Since the "rect" is always just a multiple of "cssRect" we don't need to
|
|
|
|
// check both; this function assumes that both "rect" and "cssRect" are relative
|
|
|
|
// the zoom factor in mViewportMetrics.
|
|
|
|
if (mViewportMetrics.getCssPageRect().equals(cssRect))
|
2011-11-23 11:07:29 -08:00
|
|
|
return;
|
|
|
|
|
2012-03-02 11:31:27 -08:00
|
|
|
ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
|
2012-05-23 07:49:52 -07:00
|
|
|
viewportMetrics.setPageRect(rect, cssRect);
|
2012-03-02 11:31:27 -08:00
|
|
|
mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
|
2011-11-23 11:07:29 -08:00
|
|
|
|
2012-02-17 06:56:47 -08:00
|
|
|
// Page size is owned by the layer client, so no need to notify it of
|
2011-11-23 11:07:29 -08:00
|
|
|
// this change.
|
2012-01-30 20:43:36 -08:00
|
|
|
|
|
|
|
mView.post(new Runnable() {
|
|
|
|
public void run() {
|
2012-05-23 07:49:52 -07:00
|
|
|
mPanZoomController.pageRectUpdated();
|
2012-01-30 20:43:36 -08:00
|
|
|
mView.requestRender();
|
|
|
|
}
|
|
|
|
});
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|
|
|
|
|
2011-12-07 10:48:12 -08:00
|
|
|
/**
|
|
|
|
* Sets the entire viewport metrics at once. This function does not notify the layer client or
|
|
|
|
* the pan/zoom controller, so you will need to call notifyLayerClientOfGeometryChange() or
|
2011-12-13 14:43:08 -08:00
|
|
|
* notifyPanZoomControllerOfGeometryChange() after calling this. You must hold the monitor
|
|
|
|
* while calling this.
|
2011-12-07 10:48:12 -08:00
|
|
|
*/
|
2011-11-23 11:07:29 -08:00
|
|
|
public void setViewportMetrics(ViewportMetrics viewport) {
|
2012-03-02 11:31:27 -08:00
|
|
|
mViewportMetrics = new ImmutableViewportMetrics(viewport);
|
2011-11-30 09:27:13 -08:00
|
|
|
mView.requestRender();
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|
|
|
|
|
2012-04-02 14:14:33 -07:00
|
|
|
public void setAnimationTarget(ViewportMetrics viewport) {
|
|
|
|
if (mLayerClient != null) {
|
|
|
|
// We know what the final viewport of the animation is going to be, so
|
|
|
|
// immediately request a draw of that area by setting the display port
|
|
|
|
// accordingly. This way we should have the content pre-rendered by the
|
|
|
|
// time the animation is done.
|
|
|
|
ImmutableViewportMetrics metrics = new ImmutableViewportMetrics(viewport);
|
|
|
|
DisplayPortMetrics displayPort = DisplayPortCalculator.calculate(metrics, null);
|
|
|
|
mLayerClient.adjustViewport(displayPort);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-13 14:43:08 -08:00
|
|
|
/**
|
|
|
|
* Scales the viewport, keeping the given focus point in the same place before and after the
|
|
|
|
* scale operation. You must hold the monitor while calling this.
|
|
|
|
*/
|
2011-11-15 13:41:19 -08:00
|
|
|
public void scaleWithFocus(float zoomFactor, PointF focus) {
|
2012-03-02 11:31:27 -08:00
|
|
|
ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
|
|
|
|
viewportMetrics.scaleTo(zoomFactor, focus);
|
|
|
|
mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
|
2011-11-23 11:07:47 -08:00
|
|
|
|
|
|
|
// We assume the zoom level will only be modified by the
|
|
|
|
// PanZoomController, so no need to notify it of this change.
|
|
|
|
notifyLayerClientOfGeometryChange();
|
2011-11-30 09:27:13 -08:00
|
|
|
mView.requestRender();
|
2011-11-23 11:07:47 -08:00
|
|
|
}
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
public boolean post(Runnable action) { return mView.post(action); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The view as well as the controller itself use this method to notify the layer client that
|
|
|
|
* the geometry changed.
|
|
|
|
*/
|
|
|
|
public void notifyLayerClientOfGeometryChange() {
|
|
|
|
if (mLayerClient != null)
|
|
|
|
mLayerClient.geometryChanged();
|
|
|
|
}
|
|
|
|
|
2011-12-20 13:53:39 -08:00
|
|
|
/** Aborts any pan/zoom animation that is currently in progress. */
|
2012-03-15 07:43:32 -07:00
|
|
|
public void abortPanZoomAnimation() {
|
2011-12-20 13:53:39 -08:00
|
|
|
if (mPanZoomController != null) {
|
|
|
|
mView.post(new Runnable() {
|
|
|
|
public void run() {
|
|
|
|
mPanZoomController.abortAnimation();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2011-12-07 10:48:12 -08:00
|
|
|
}
|
|
|
|
|
2011-11-18 10:28:17 -08:00
|
|
|
/**
|
|
|
|
* Returns true if this controller is fine with performing a redraw operation or false if it
|
|
|
|
* would prefer that the action didn't take place.
|
|
|
|
*/
|
|
|
|
public boolean getRedrawHint() {
|
2011-11-23 11:08:20 -08:00
|
|
|
if (mForceRedraw) {
|
|
|
|
mForceRedraw = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-02-28 13:14:14 -08:00
|
|
|
if (!mPanZoomController.getRedrawHint()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-03-26 10:15:50 -07:00
|
|
|
return DisplayPortCalculator.aboutToCheckerboard(mViewportMetrics,
|
|
|
|
mPanZoomController.getVelocityVector(), mLayerClient.getDisplayPort());
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts a point from layer view coordinates to layer coordinates. In other words, given a
|
|
|
|
* point measured in pixels from the top left corner of the layer view, returns the point in
|
2012-03-19 21:07:42 -07:00
|
|
|
* pixels measured from the last scroll position we sent to Gecko, in CSS pixels. Assuming the
|
|
|
|
* events being sent to Gecko are processed in FIFO order, this calculation should always be
|
|
|
|
* correct.
|
2011-11-18 10:28:17 -08:00
|
|
|
*/
|
|
|
|
public PointF convertViewPointToLayerPoint(PointF viewPoint) {
|
2012-04-04 08:55:40 -07:00
|
|
|
if (mLayerClient == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2012-03-02 11:31:27 -08:00
|
|
|
ImmutableViewportMetrics viewportMetrics = mViewportMetrics;
|
|
|
|
PointF origin = viewportMetrics.getOrigin();
|
|
|
|
float zoom = viewportMetrics.zoomFactor;
|
2012-03-19 21:07:42 -07:00
|
|
|
ViewportMetrics geckoViewport = mLayerClient.getGeckoViewportMetrics();
|
|
|
|
PointF geckoOrigin = geckoViewport.getOrigin();
|
|
|
|
float geckoZoom = geckoViewport.getZoomFactor();
|
2012-03-12 13:20:19 -07:00
|
|
|
|
|
|
|
// viewPoint + origin gives the coordinate in device pixels from the top-left corner of the page.
|
|
|
|
// Divided by zoom, this gives us the coordinate in CSS pixels from the top-left corner of the page.
|
2012-03-19 21:07:42 -07:00
|
|
|
// geckoOrigin / geckoZoom is where Gecko thinks it is (scrollTo position) in CSS pixels from
|
2012-03-12 13:20:19 -07:00
|
|
|
// the top-left corner of the page. Subtracting the two gives us the offset of the viewPoint from
|
|
|
|
// the current Gecko coordinate in CSS pixels.
|
|
|
|
PointF layerPoint = new PointF(
|
2012-03-19 21:07:42 -07:00
|
|
|
((viewPoint.x + origin.x) / zoom) - (geckoOrigin.x / geckoZoom),
|
|
|
|
((viewPoint.y + origin.y) / zoom) - (geckoOrigin.y / geckoZoom));
|
2012-03-12 13:20:19 -07:00
|
|
|
|
|
|
|
return layerPoint;
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|
|
|
|
|
2012-02-03 12:35:01 -08:00
|
|
|
/** Retrieves whether we should show checkerboard checks or not. */
|
|
|
|
public boolean checkerboardShouldShowChecks() {
|
|
|
|
return mCheckerboardShouldShowChecks;
|
|
|
|
}
|
|
|
|
|
2012-01-23 20:10:24 -08:00
|
|
|
/** Retrieves the color that the checkerboard should be. */
|
|
|
|
public int getCheckerboardColor() {
|
|
|
|
return mCheckerboardColor;
|
|
|
|
}
|
|
|
|
|
2012-02-03 12:35:01 -08:00
|
|
|
/** Sets whether or not the checkerboard should show checkmarks. */
|
|
|
|
public void setCheckerboardShowChecks(boolean showChecks) {
|
|
|
|
mCheckerboardShouldShowChecks = showChecks;
|
|
|
|
mView.requestRender();
|
|
|
|
}
|
|
|
|
|
2012-01-23 20:10:24 -08:00
|
|
|
/** Sets a new color for the checkerboard. */
|
|
|
|
public void setCheckerboardColor(int newColor) {
|
|
|
|
mCheckerboardColor = newColor;
|
|
|
|
mView.requestRender();
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|
2012-05-18 08:24:27 -07:00
|
|
|
|
2012-05-18 08:24:27 -07:00
|
|
|
public void setAllowZoom(final boolean aValue) {
|
2012-05-18 08:24:27 -07:00
|
|
|
mAllowZoom = aValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean getAllowZoom() {
|
|
|
|
return mAllowZoom;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setDefaultZoom(float aValue) {
|
|
|
|
mDefaultZoom = aValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
public float getDefaultZoom() {
|
|
|
|
return mDefaultZoom;
|
|
|
|
}
|
2012-05-18 08:24:27 -07:00
|
|
|
|
|
|
|
public void setMinZoom(float aValue) {
|
|
|
|
mMinZoom = aValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
public float getMinZoom() {
|
|
|
|
return mMinZoom;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setMaxZoom(float aValue) {
|
|
|
|
mMaxZoom = aValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
public float getMaxZoom() {
|
|
|
|
return mMaxZoom;
|
|
|
|
}
|
2011-11-18 10:28:17 -08:00
|
|
|
}
|