Bug 703141 - Refactor around the idea of a viewport and displayport. r=kats

This patch refactors the code to make some of the value names and ownership
clearer, and to add the idea of a 'viewport' within a 'displayport'. The
displayport is the area of the page which is visible to the underlying buffer
and the viewport is the area of the page which is visible through the
application window.

--HG--
rename : mobile/android/base/ui/ViewportController.java => mobile/android/base/gfx/ViewportMetrics.java
This commit is contained in:
Chris Lord 2011-11-23 19:07:29 +00:00
Родитель b60ba3c957
Коммит c5ca0523a3
16 изменённых файлов: 526 добавлений и 374 удалений

Просмотреть файл

@ -46,6 +46,7 @@ import org.mozilla.gecko.gfx.LayerController;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.gfx.PlaceholderLayerClient;
import org.mozilla.gecko.gfx.RectUtils;
import org.mozilla.gecko.gfx.ViewportMetrics;
import org.mozilla.gecko.Tab.HistoryEntry;
import java.io.*;
@ -514,6 +515,10 @@ abstract public class GeckoApp
return file.toString();
}
public SharedPreferences getPlaceholderPrefs() {
return getSharedPreferences("GeckoApp", MODE_PRIVATE);
}
private void rememberLastScreen(boolean sync) {
if (mUserDefinedProfile)
return;
@ -526,7 +531,14 @@ abstract public class GeckoApp
if (lastHistoryEntry == null)
return;
SharedPreferences prefs = getSharedPreferences("GeckoApp", 0);
if (getLayerController().getLayerClient() != mSoftwareLayerClient)
return;
IntSize pageSize = getLayerController().getPageSize();
Rect visibleArea = getLayerController().getViewport();
Point offset = getLayerController().getViewportMetrics().getViewportOffset();
SharedPreferences prefs = getPlaceholderPrefs();
Editor editor = prefs.edit();
String uri = lastHistoryEntry.mUri;
@ -535,7 +547,6 @@ abstract public class GeckoApp
String lastUri = prefs.getString("last-uri", "");
String lastTitle = prefs.getString("last-title", uri);
// see if we can bail.
if (uri.equals(lastUri) && title.equals(lastTitle))
return;
@ -543,6 +554,23 @@ abstract public class GeckoApp
editor.putString("last-uri", uri);
editor.putString("last-title", title);
if (pageSize != null) {
editor.putInt("page-width", pageSize.width);
editor.putInt("page-height", pageSize.height);
}
if (visibleArea != null) {
editor.putInt("viewport-left", visibleArea.left);
editor.putInt("viewport-top", visibleArea.top);
editor.putInt("viewport-right", visibleArea.right);
editor.putInt("viewport-bottom", visibleArea.bottom);
}
if (offset != null) {
editor.putInt("viewport-offset-x", offset.x);
editor.putInt("viewport-offset-y", offset.y);
}
Log.i(LOGTAG, "Saving:: " + uri + " " + title);
editor.commit();
@ -820,16 +848,6 @@ abstract public class GeckoApp
GeckoAppShell.sendEventToGecko(getPrefsEvent);
connectGeckoLayerClient();
} else if (event.equals("PanZoom:Resize")) {
IntSize size = new IntSize(message.getJSONObject("size"));
int layoutWidth = mGeckoLayout.getMeasuredWidth();
int layoutHeight = mGeckoLayout.getMeasuredHeight();
// increase page size if smaller than layout dimensions
if (size.width < layoutWidth || size.height < layoutHeight) {
size = new IntSize(Math.max(size.width, layoutWidth),
Math.max(size.height, layoutHeight));
}
mSoftwareLayerClient.setPageSize(size);
} else if (event.equals("ToggleChrome:Hide")) {
mMainHandler.post(new Runnable() {
public void run() {
@ -844,7 +862,7 @@ abstract public class GeckoApp
});
}
} catch (Exception e) {
Log.i(LOGTAG, "handleMessage throws " + e + " for message: " + event);
Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
}
}
@ -887,6 +905,7 @@ abstract public class GeckoApp
final Tab tab = Tabs.getInstance().addTab(tabId, uri);
if (selected) {
Tabs.getInstance().selectTab(tabId);
mSoftwareLayerClient.geckoLoadedNewContent();
}
mMainHandler.post(new Runnable() {
@ -919,6 +938,8 @@ abstract public class GeckoApp
if (tab == null)
return;
mSoftwareLayerClient.geckoLoadedNewContent();
mMainHandler.post(new Runnable() {
public void run() {
if (Tabs.getInstance().isSelectedTab(tab)) {
@ -994,6 +1015,8 @@ abstract public class GeckoApp
if (Tabs.getInstance().isSelectedTab(tab))
mBrowserToolbar.setTitle(tab.getDisplayTitle());
onTabsChanged(tab);
mSoftwareLayerClient.geckoLoadedNewContent();
}
});
}
@ -1044,7 +1067,7 @@ abstract public class GeckoApp
if (mGeckoLayout.indexOfChild(view) == -1) {
lp = new PluginLayoutParams((int) w, (int) h, (int)x, (int)y);
lp.repositionFromVisibleRect(mLayerController.getVisibleRect(), mLayerController.getZoomFactor(), true);
lp.repositionFromVisibleRect(mLayerController.getViewport(), 1.0f/*mLayerController.getZoomFactor()*/, true);
view.setWillNotDraw(false);
if (view instanceof SurfaceView) {
@ -1059,7 +1082,7 @@ abstract public class GeckoApp
} else {
lp = (PluginLayoutParams)view.getLayoutParams();
lp.reset((int)x, (int)y, (int)w, (int)h);
lp.repositionFromVisibleRect(mLayerController.getVisibleRect(), mLayerController.getZoomFactor(), true);
lp.repositionFromVisibleRect(mLayerController.getViewport(), 1.0f/*mLayerController.getZoomFactor()*/, true);
try {
mGeckoLayout.updateViewLayout(view, lp);
} catch (IllegalArgumentException e) {
@ -1101,7 +1124,7 @@ abstract public class GeckoApp
public void repositionPluginViews(boolean resize, boolean setVisible) {
for (View view : mPluginViews) {
PluginLayoutParams lp = (PluginLayoutParams)view.getLayoutParams();
lp.repositionFromVisibleRect(mLayerController.getVisibleRect(), mLayerController.getZoomFactor(), resize);
lp.repositionFromVisibleRect(mLayerController.getViewport(), 1.0f/*mLayerController.getZoomFactor()*/, resize);
if (setVisible) {
view.setVisibility(View.VISIBLE);
@ -1241,7 +1264,6 @@ abstract public class GeckoApp
GeckoAppShell.registerGeckoEventListener("Preferences:Data", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("Gecko:Ready", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("Toast:Show", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("PanZoom:Resize", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("ToggleChrome:Hide", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("ToggleChrome:Show", GeckoApp.mAppContext);
@ -1909,7 +1931,7 @@ abstract public class GeckoApp
height = originalHeight = aHeight;
}
public void repositionFromVisibleRect(RectF rect, float zoomFactor, boolean resize) {
public void repositionFromVisibleRect(Rect rect, float zoomFactor, boolean resize) {
x = (int)((originalX - rect.left) * zoomFactor);
y = (int)((originalY - rect.top) * zoomFactor);

Просмотреть файл

@ -477,10 +477,7 @@ public class GeckoAppShell
}
});
GeckoEvent event = new GeckoEvent(GeckoEvent.SIZE_CHANGED,
LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT,
LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT);
GeckoAppShell.sendEventToGecko(event);
layerController.notifyLayerClientOfGeometryChange();
}
private static void sendPendingEventsToGecko() {
try {

Просмотреть файл

@ -94,8 +94,8 @@ JAVAFILES = \
gfx/TextureReaper.java \
gfx/TextLayer.java \
gfx/TileLayer.java \
gfx/ViewportMetrics.java \
ui/PanZoomController.java \
ui/ViewportController.java \
$(NULL)
PROCESSEDJAVAFILES = \

Просмотреть файл

@ -20,6 +20,7 @@
*
* 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
@ -43,7 +44,6 @@ import org.mozilla.gecko.gfx.LayerClient;
import org.mozilla.gecko.gfx.LayerController;
import org.mozilla.gecko.gfx.LayerRenderer;
import org.mozilla.gecko.gfx.SingleTileLayer;
import org.mozilla.gecko.ui.ViewportController;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
@ -52,6 +52,7 @@ import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.DisplayMetrics;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
@ -66,14 +67,21 @@ import java.util.TimerTask;
* TODO: Throttle down Gecko's priority when we pan and zoom.
*/
public class GeckoSoftwareLayerClient extends LayerClient {
private static final String LOGTAG = "GeckoSoftwareLayerClient";
private Context mContext;
private int mWidth, mHeight, mFormat;
private IntSize mScreenSize, mViewportSize;
private ByteBuffer mBuffer;
private final SingleTileLayer mTileLayer;
private ViewportController mViewportController;
/* The viewport rect that Gecko is currently displaying. */
private RectF mGeckoVisibleRect;
private ViewportMetrics mGeckoViewport;
/* Gecko has loaded new content, let its viewport metrics completely
* override the LayerController on the next draw.
*/
private boolean mNewContent;
private CairoImage mCairoImage;
@ -81,20 +89,16 @@ public class GeckoSoftwareLayerClient extends LayerClient {
private long mLastViewportChangeTime;
private Timer mViewportRedrawTimer;
/* The initial page width and height that we use before a page is loaded. */
private static final int PAGE_WIDTH = 980; /* Matches MobileSafari. */
private static final int PAGE_HEIGHT = 1500;
public GeckoSoftwareLayerClient(Context context) {
mContext = context;
mViewportController = new ViewportController(new IntSize(PAGE_WIDTH, PAGE_HEIGHT),
new RectF(0, 0, 1, 1));
mWidth = LayerController.TILE_WIDTH;
mHeight = LayerController.TILE_HEIGHT;
mFormat = CairoImage.FORMAT_RGB16_565;
mScreenSize = new IntSize(1, 1);
mNewContent = true;
mBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 2);
mCairoImage = new CairoImage() {
@ -113,9 +117,13 @@ public class GeckoSoftwareLayerClient extends LayerClient {
/** Attaches the root layer to the layer controller so that Gecko appears. */
@Override
public void init() {
getLayerController().setRoot(mTileLayer);
render();
public void setLayerController(LayerController layerController) {
super.setLayerController(layerController);
layerController.setRoot(mTileLayer);
if (mGeckoViewport != null)
layerController.setViewportMetrics(mGeckoViewport);
geometryChanged();
}
public void beginDrawing() {
@ -131,13 +139,22 @@ public class GeckoSoftwareLayerClient extends LayerClient {
LayerController controller = getLayerController();
if (controller == null)
return;
controller.notifyViewOfGeometryChange();
try {
JSONObject metadataObject = new JSONObject(metadata);
float originX = (float)metadataObject.getDouble("x");
float originY = (float)metadataObject.getDouble("y");
mTileLayer.setOrigin(new PointF(originX, originY));
mGeckoViewport = new ViewportMetrics(metadataObject);
mTileLayer.setOrigin(mGeckoViewport.getDisplayportOrigin());
if (controller != null) {
if (mNewContent) {
mNewContent = false;
controller.setViewportMetrics(mGeckoViewport);
} else {
controller.setPageSize(mGeckoViewport.getPageSize());
}
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
@ -149,6 +166,10 @@ public class GeckoSoftwareLayerClient extends LayerClient {
}
}
public void geckoLoadedNewContent() {
mNewContent = true;
}
/** Returns the back buffer. This function is for Gecko to use. */
public ByteBuffer lockBuffer() {
return mBuffer;
@ -162,20 +183,24 @@ public class GeckoSoftwareLayerClient extends LayerClient {
/* no-op */
}
/** Called whenever the page changes size. */
public void setPageSize(IntSize pageSize) {
mViewportController.setPageSize(pageSize);
getLayerController().setPageSize(pageSize);
}
@Override
public void geometryChanged() {
mViewportController.setVisibleRect(getTransformedVisibleRect());
render();
/* Let Gecko know if the screensize has changed */
DisplayMetrics metrics = new DisplayMetrics();
GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
if (metrics.widthPixels != mScreenSize.width ||
metrics.heightPixels != mScreenSize.height) {
mScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
Log.i(LOGTAG, "Screen-size changed to " + mScreenSize);
GeckoEvent event = new GeckoEvent(GeckoEvent.SIZE_CHANGED,
LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT,
metrics.widthPixels, metrics.heightPixels);
GeckoAppShell.sendEventToGecko(event);
}
@Override
public IntSize getPageSize() { return mViewportController.getPageSize(); }
render();
}
@Override
public void render() {
@ -211,24 +236,14 @@ public class GeckoSoftwareLayerClient extends LayerClient {
}
private void adjustViewport() {
LayerController layerController = getLayerController();
RectF visibleRect = layerController.getVisibleRect();
RectF tileRect = mViewportController.widenRect(visibleRect);
tileRect = mViewportController.clampRect(tileRect);
ViewportMetrics viewportMetrics = getLayerController().getViewportMetrics();
Point viewportOffset = viewportMetrics.getOptimumViewportOffset();
viewportMetrics.setViewportOffset(viewportOffset);
int x = (int)Math.round(tileRect.left), y = (int)Math.round(tileRect.top);
GeckoEvent event = new GeckoEvent("Viewport:Change", "{\"x\": " + x +
", \"y\": " + y + "}");
GeckoEvent event = new GeckoEvent("Viewport:Change", viewportMetrics.toJSON());
GeckoAppShell.sendEventToGecko(event);
mLastViewportChangeTime = System.currentTimeMillis();
}
/* Returns the dimensions of the box in page coordinates that the user is viewing. */
private RectF getTransformedVisibleRect() {
LayerController layerController = getLayerController();
return mViewportController.transformVisibleRect(layerController.getVisibleRect(),
layerController.getPageSize());
}
}

Просмотреть файл

@ -43,6 +43,7 @@ import org.json.JSONObject;
public class IntSize {
public final int width, height;
public IntSize(IntSize size) { width = size.width; height = size.height; }
public IntSize(int inWidth, int inHeight) { width = inWidth; height = inHeight; }
public IntSize(JSONObject json) {
@ -54,6 +55,10 @@ public class IntSize {
}
}
public boolean equals(IntSize size) {
return ((size.width == width) && (size.height == height));
}
@Override
public String toString() { return "(" + width + "," + height + ")"; }

Просмотреть файл

@ -37,7 +37,7 @@
package org.mozilla.gecko.gfx;
import android.graphics.PointF;
import android.graphics.Point;
import android.util.Log;
import java.util.concurrent.locks.ReentrantLock;
import javax.microedition.khronos.opengles.GL10;
@ -45,12 +45,12 @@ import javax.microedition.khronos.opengles.GL10;
public abstract class Layer {
private final ReentrantLock mTransactionLock;
private boolean mInTransaction;
private PointF mOrigin;
private PointF mNewOrigin;
private Point mOrigin;
private Point mNewOrigin;
public Layer() {
mTransactionLock = new ReentrantLock();
mOrigin = new PointF(0.0f, 0.0f);
mOrigin = new Point(0, 0);
}
/** Draws the layer. Automatically applies the translation. */
@ -104,12 +104,12 @@ public abstract class Layer {
}
/** Returns the current layer origin. */
public PointF getOrigin() {
public Point getOrigin() {
return mOrigin;
}
/** Sets the origin. Only valid inside a transaction. */
public void setOrigin(PointF newOrigin) {
public void setOrigin(Point newOrigin) {
if (!mInTransaction)
throw new RuntimeException("setOrigin() is only valid inside a transaction");
mNewOrigin = newOrigin;

Просмотреть файл

@ -37,9 +37,6 @@
package org.mozilla.gecko.gfx;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.LayerController;
/**
* A layer client provides tiles and manages other information used by the layer controller.
*/
@ -47,12 +44,6 @@ public abstract class LayerClient {
private LayerController mLayerController;
public abstract void geometryChanged();
public abstract IntSize getPageSize();
/** Called whenever the page changes size. */
public abstract void setPageSize(IntSize pageSize);
public abstract void init();
protected abstract void render();
public LayerController getLayerController() { return mLayerController; }

Просмотреть файл

@ -20,6 +20,7 @@
*
* 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
@ -47,6 +48,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@ -55,6 +57,7 @@ import android.view.MotionEvent;
import android.view.GestureDetector;
import android.view.ScaleGestureDetector;
import android.view.View.OnTouchListener;
import java.lang.Math;
import java.util.ArrayList;
/**
@ -66,9 +69,7 @@ public class LayerController {
private Layer mRootLayer; /* The root layer. */
private LayerView mView; /* The main rendering view. */
private Context mContext; /* The current context. */
private RectF mVisibleRect; /* The current visible region. */
private IntSize mScreenSize; /* The screen size of the viewport. */
private IntSize mPageSize; /* The current page size. */
private ViewportMetrics mViewportMetrics; /* The current viewport metrics. */
private PanZoomController mPanZoomController;
/*
@ -79,24 +80,19 @@ public class LayerController {
private OnTouchListener mOnTouchListener; /* The touch listener. */
private LayerClient mLayerClient; /* The layer client. */
/* NB: These must be powers of two due to the OpenGL ES 1.x restriction on NPOT textures. */
public static final int TILE_WIDTH = 1024;
public static final int TILE_HEIGHT = 2048;
/* NB: These must be powers of two due to the OpenGL ES 1.x restriction on NPOT textures. */
private static final int DANGER_ZONE_X = 150;
private static final int DANGER_ZONE_Y = 300;
/* If the visible rect is within the danger zone (measured in pixels from each edge of a tile),
* we start aggressively redrawing to minimize checkerboarding. */
private static final int DANGER_ZONE_X = 150;
private static final int DANGER_ZONE_Y = 300;
public LayerController(Context context) {
mContext = context;
mVisibleRect = new RectF(0.0f, 0.0f, 1.0f, 1.0f);
/* Gets filled in when the surface changes. */
mScreenSize = new IntSize(1, 1);
mPageSize = new IntSize(LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT);
mViewportMetrics = new ViewportMetrics();
mPanZoomController = new PanZoomController(this);
mView = new LayerView(context, this);
}
@ -105,17 +101,27 @@ public class LayerController {
public void setLayerClient(LayerClient layerClient) {
mLayerClient = layerClient;
mPageSize = layerClient.getPageSize();
layerClient.setLayerController(this);
layerClient.init();
}
public LayerClient getLayerClient() { return mLayerClient; }
public Layer getRoot() { return mRootLayer; }
public LayerView getView() { return mView; }
public Context getContext() { return mContext; }
public RectF getVisibleRect() { return mVisibleRect; }
public IntSize getScreenSize() { return mScreenSize; }
public IntSize getPageSize() { return mPageSize; }
public ViewportMetrics getViewportMetrics() { return mViewportMetrics; }
public Rect getViewport() {
return mViewportMetrics.getViewport();
}
public IntSize getViewportSize() {
Rect viewport = getViewport();
return new IntSize(viewport.width(), viewport.height());
}
public IntSize getPageSize() {
return mViewportMetrics.getPageSize();
}
public Bitmap getCheckerboardPattern() { return getDrawable("checkerboard"); }
public Bitmap getShadowPattern() { return getDrawable("shadow"); }
@ -131,60 +137,51 @@ public class LayerController {
return BitmapFactory.decodeResource(mContext.getResources(), resourceID, options);
}
/*
* Note that the zoom factor of the layer controller differs from the zoom factor of the layer
* client (i.e. the page).
*/
public float getZoomFactor() { return (float)mScreenSize.width / mVisibleRect.width(); }
/**
* The view calls this to indicate that the screen changed size.
* The view calls this to indicate that the viewport changed size.
*
* 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.
*/
public void setScreenSize(int width, int height) {
float zoomFactor = getZoomFactor(); /* Must come first. */
mScreenSize = new IntSize(width, height);
setVisibleRect(mVisibleRect.left, mVisibleRect.top, width / zoomFactor,
height / zoomFactor);
public void setViewportSize(IntSize size) {
mViewportMetrics.setSize(size);
notifyLayerClientOfGeometryChange();
mPanZoomController.geometryChanged();
}
public void setNeedsDisplay() {
// TODO
public void scrollTo(PointF point) {
mViewportMetrics.setOrigin(new Point(Math.round(point.x),
Math.round(point.y)));
notifyLayerClientOfGeometryChange();
mPanZoomController.geometryChanged();
}
public void scrollTo(float x, float y) {
setVisibleRect(x, y, mVisibleRect.width(), mVisibleRect.height());
}
public void setVisibleRect(float x, float y, float width, float height) {
mVisibleRect = new RectF(x, y, x + width, y + height);
setNeedsDisplay();
GeckoApp.mAppContext.repositionPluginViews(false);
}
/**
* Sets the zoom factor to 1, adjusting the visible rect accordingly. The Gecko layer client
* calls this function after a zoom has completed and Gecko is done rendering the new visible
* region.
*/
public void unzoom() {
float zoomFactor = getZoomFactor();
float x = Math.round(mVisibleRect.left * zoomFactor);
float y = Math.round(mVisibleRect.top * zoomFactor);
mVisibleRect = new RectF(x, y, x + mScreenSize.width, y + mScreenSize.height);
mPageSize = mPageSize.scale(zoomFactor);
setNeedsDisplay();
public void setViewport(Rect viewport) {
mViewportMetrics.setViewport(viewport);
notifyLayerClientOfGeometryChange();
mPanZoomController.geometryChanged();
}
public void setPageSize(IntSize size) {
mPageSize = size.scale(getZoomFactor());
mView.notifyRendererOfPageSizeChange();
if (mViewportMetrics.getPageSize().equals(size))
return;
mViewportMetrics.setPageSize(size);
// Page size is owned by the LayerClient, so no need to notify it of
// this change.
mPanZoomController.geometryChanged();
}
public void setViewportMetrics(ViewportMetrics viewport) {
mViewportMetrics = new ViewportMetrics(viewport);
// We assume this was called by the LayerClient (as it includes page
// size), so no need to notify it of this change.
mPanZoomController.geometryChanged();
}
public boolean post(Runnable action) { return mView.post(action); }
@ -202,12 +199,6 @@ public class LayerController {
mLayerClient.geometryChanged();
}
// Informs the view and the panning and zooming controller that the geometry changed.
public void notifyViewOfGeometryChange() {
mView.geometryChanged();
mPanZoomController.geometryChanged();
}
/**
* 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.
@ -223,38 +214,9 @@ public class LayerController {
// Returns true if a checkerboard is about to be visible.
private boolean aboutToCheckerboard() {
Rect pageRect = new Rect(0, 0, mPageSize.width, mPageSize.height);
Rect adjustedPageRect = RectUtils.contract(pageRect, DANGER_ZONE_X, DANGER_ZONE_Y);
RectF visiblePageRect = RectUtils.intersect(mVisibleRect, new RectF(adjustedPageRect));
RectF adjustedTileRect = RectUtils.contract(getTileRect(), DANGER_ZONE_X, DANGER_ZONE_Y);
return !adjustedTileRect.contains(visiblePageRect);
}
/** Returns the given rect, clamped to the boundaries of a tile. */
public RectF clampRect(RectF rect) {
float x = clamp(0, rect.left, mPageSize.width - LayerController.TILE_WIDTH);
float y = clamp(0, rect.top, mPageSize.height - LayerController.TILE_HEIGHT);
return new RectF(x, y, x + rect.width(), y + rect.height());
}
private float clamp(float min, float value, float max) {
if (max < min)
return min;
return (value < min) ? min : (value > max) ? max : value;
}
// Returns the coordinates of a tile, scaled by the given factor, centered on the given rect.
private static RectF widenRect(RectF rect, float scaleFactor) {
PointF center = new PointF(rect.centerX(), rect.centerY());
float halfTileWidth = TILE_WIDTH * scaleFactor / 2.0f;
float halfTileHeight = TILE_HEIGHT * scaleFactor / 2.0f;
return new RectF(rect.centerX() - halfTileWidth, rect.centerY() - halfTileHeight,
rect.centerX() + halfTileWidth, rect.centerY() + halfTileHeight);
}
/** Returns the coordinates of a tile centered on the given rect. */
public static RectF widenRect(RectF rect) {
return widenRect(rect, 1.0f);
RectF adjustedTileRect =
RectUtils.contract(getTileRect(), DANGER_ZONE_X, DANGER_ZONE_Y);
return !adjustedTileRect.contains(new RectF(mViewportMetrics.getViewport()));
}
/**
@ -269,17 +231,19 @@ public class LayerController {
return null;
// Undo the transforms.
PointF scaledPoint = PointUtils.scale(viewPoint, 1.0f / getZoomFactor());
return PointUtils.subtract(PointUtils.add(new PointF(mVisibleRect.left, mVisibleRect.top),
scaledPoint),
mRootLayer.getOrigin());
PointF newPoint = new PointF(mViewportMetrics.getOrigin());
newPoint.offset(viewPoint.x, viewPoint.y);
Point rootOrigin = mRootLayer.getOrigin();
newPoint.offset(-rootOrigin.x, -rootOrigin.y);
return newPoint;
}
/*
* Gesture detection. This is handled only at a high level in this class; we dispatch to the
* pan/zoom controller to do the dirty work.
*/
public boolean onTouchEvent(MotionEvent event) {
if (mPanZoomController.onTouchEvent(event))
return true;

Просмотреть файл

@ -20,6 +20,7 @@
*
* 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
@ -119,7 +120,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
/* Draw the checkerboard. */
Rect clampedPageRect = clampToScreen(pageRect);
IntSize screenSize = controller.getScreenSize();
IntSize screenSize = controller.getViewportSize();
gl.glEnable(GL10.GL_SCISSOR_TEST);
gl.glScissor(clampedPageRect.left, screenSize.height - clampedPageRect.bottom,
clampedPageRect.width(), clampedPageRect.height());
@ -155,28 +156,24 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
gl.glDisable(GL10.GL_BLEND);
}
public void pageSizeChanged() {
mShadowLayer.recreateVertexBuffers();
}
private void setupPageTransform(GL10 gl) {
LayerController controller = mView.getController();
RectF visibleRect = controller.getVisibleRect();
float zoomFactor = controller.getZoomFactor();
Rect viewport = controller.getViewport();
//float zoomFactor = controller.getZoomFactor();
gl.glLoadIdentity();
gl.glScalef(zoomFactor, zoomFactor, 1.0f);
gl.glTranslatef(-visibleRect.left, -visibleRect.top, 0.0f);
//gl.glScalef(zoomFactor, zoomFactor, 1.0f);
gl.glTranslatef(-viewport.left, -viewport.top, 0.0f);
}
private Rect getPageRect() {
LayerController controller = mView.getController();
float zoomFactor = controller.getZoomFactor();
RectF visibleRect = controller.getVisibleRect();
float zoomFactor = 1.0f;//controller.getZoomFactor();
Rect viewport = controller.getViewport();
IntSize pageSize = controller.getPageSize();
int x = (int)Math.round(-zoomFactor * visibleRect.left);
int y = (int)Math.round(-zoomFactor * visibleRect.top);
int x = (int)Math.round(-zoomFactor * viewport.left);
int y = (int)Math.round(-zoomFactor * viewport.top);
return new Rect(x, y,
x + (int)Math.round(zoomFactor * pageSize.width),
y + (int)Math.round(zoomFactor * pageSize.height));
@ -184,7 +181,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
private Rect clampToScreen(Rect rect) {
LayerController controller = mView.getController();
IntSize screenSize = controller.getScreenSize();
IntSize screenSize = controller.getViewportSize();
int left = Math.max(0, rect.left);
int top = Math.max(0, rect.top);
@ -205,7 +202,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
// done on the main UI thread, not the GL renderer thread
mView.post(new Runnable() {
public void run() {
mView.setScreenSize(width, height);
mView.setViewportSize(new IntSize(width, height));
}
});

Просмотреть файл

@ -88,15 +88,10 @@ public class LayerView extends GLSurfaceView {
}
public LayerController getController() { return mController; }
public void geometryChanged() { /* TODO: Schedule a redraw. */ }
public void notifyRendererOfPageSizeChange() {
mRenderer.pageSizeChanged();
}
/** The LayerRenderer calls this to indicate that the window has changed size. */
public void setScreenSize(int width, int height) {
mController.setScreenSize(width, height);
public void setViewportSize(IntSize size) {
mController.setViewportSize(size);
}
public void setInputConnectionHandler(InputConnectionHandler handler) {

Просмотреть файл

@ -50,6 +50,7 @@ public class NinePatchTileLayer extends TileLayer {
private FloatBuffer mSideTexCoordBuffer, mSideVertexBuffer;
private FloatBuffer mTopTexCoordBuffer, mTopVertexBuffer;
private LayerController mLayerController;
private IntSize mPageSize;
private static final int PATCH_SIZE = 16;
private static final int TEXTURE_SIZE = 48;
@ -90,6 +91,7 @@ public class NinePatchTileLayer extends TileLayer {
public NinePatchTileLayer(LayerController layerController, CairoImage image) {
super(false, image);
mPageSize = new IntSize(1, 1);
mLayerController = layerController;
mSideTexCoordBuffer = createBuffer(SIDE_TEX_COORDS);
@ -99,24 +101,22 @@ public class NinePatchTileLayer extends TileLayer {
}
public void recreateVertexBuffers() {
IntSize pageSize = mLayerController.getPageSize();
float[] sideVertices = {
-PATCH_SIZE, -PATCH_SIZE, 0.0f,
0.0f, -PATCH_SIZE, 0.0f,
-PATCH_SIZE, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f,
-PATCH_SIZE, pageSize.height, 0.0f,
0.0f, pageSize.height, 0.0f,
-PATCH_SIZE, PATCH_SIZE + pageSize.height, 0.0f,
0.0f, PATCH_SIZE + pageSize.height, 0.0f
-PATCH_SIZE, mPageSize.height, 0.0f,
0.0f, mPageSize.height, 0.0f,
-PATCH_SIZE, PATCH_SIZE + mPageSize.height, 0.0f,
0.0f, PATCH_SIZE + mPageSize.height, 0.0f
};
float[] topVertices = {
0.0f, -PATCH_SIZE, 0.0f,
pageSize.width, -PATCH_SIZE, 0.0f,
mPageSize.width, -PATCH_SIZE, 0.0f,
0.0f, 0.0f, 0.0f,
pageSize.width, 0.0f, 0.0f
mPageSize.width, 0.0f, 0.0f
};
mSideVertexBuffer = createBuffer(sideVertices);
@ -126,6 +126,10 @@ public class NinePatchTileLayer extends TileLayer {
@Override
protected void onTileDraw(GL10 gl) {
IntSize pageSize = mLayerController.getPageSize();
if (!pageSize.equals(mPageSize)) {
mPageSize = pageSize;
recreateVertexBuffers();
}
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
gl.glEnable(GL10.GL_BLEND);

Просмотреть файл

@ -44,8 +44,11 @@ import org.mozilla.gecko.gfx.LayerClient;
import org.mozilla.gecko.gfx.SingleTileLayer;
import org.mozilla.gecko.GeckoApp;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;
@ -58,27 +61,33 @@ import java.nio.ByteBuffer;
*/
public class PlaceholderLayerClient extends LayerClient {
private Context mContext;
private IntSize mPageSize;
private ViewportMetrics mViewport;
private int mWidth, mHeight, mFormat;
private ByteBuffer mBuffer;
private FetchImageTask mTask;
private PlaceholderLayerClient(Context context) {
mContext = context;
mPageSize = new IntSize(995, 1250); /* TODO */
SharedPreferences prefs = GeckoApp.mAppContext.getPlaceholderPrefs();
IntSize pageSize = new IntSize(prefs.getInt("page-width", 995),
prefs.getInt("page-height", 1250));
Rect viewport = new Rect(prefs.getInt("viewport-left", 0),
prefs.getInt("viewport-top", 0),
prefs.getInt("viewport-right", 1),
prefs.getInt("viewport-bottom", 1));
Point offset = new Point(prefs.getInt("viewport-offset-x", 0),
prefs.getInt("viewport-offset-y", 0));
mViewport = new ViewportMetrics();
mViewport.setPageSize(pageSize);
mViewport.setViewport(viewport);
mViewport.setViewportOffset(offset);
}
public static PlaceholderLayerClient createInstance(Context context) {
return new PlaceholderLayerClient(context);
}
public void init() {
// Until http://code.google.com/p/android/issues/detail?id=16941
// is worked around, do not load the last screenshot as it will OOM
// mTask = new FetchImageTask();
// mTask.execute();
}
public void destroy() {
if (mTask != null) {
mTask.cancel(false);
@ -114,6 +123,11 @@ public class PlaceholderLayerClient extends LayerClient {
protected void onPostExecute(Void unused) {
BufferedCairoImage image = new BufferedCairoImage(mBuffer, mWidth, mHeight, mFormat);
SingleTileLayer tileLayer = new SingleTileLayer(image);
tileLayer.beginTransaction();
tileLayer.setOrigin(mViewport.getDisplayportOrigin());
tileLayer.endTransaction();
getLayerController().setRoot(tileLayer);
}
}
@ -121,12 +135,17 @@ public class PlaceholderLayerClient extends LayerClient {
@Override
public void geometryChanged() { /* no-op */ }
@Override
public IntSize getPageSize() { return mPageSize; }
@Override
public void render() { /* no-op */ }
/** Called whenever the page changes size. */
@Override
public void setPageSize(IntSize pageSize) { mPageSize = pageSize; }
public void setLayerController(LayerController layerController) {
super.setLayerController(layerController);
layerController.setViewportMetrics(mViewport);
BufferedCairoImage image = new BufferedCairoImage(mBuffer, mWidth, mHeight, mFormat);
SingleTileLayer tileLayer = new SingleTileLayer(image);
layerController.setRoot(tileLayer);
}
}

Просмотреть файл

@ -0,0 +1,202 @@
/* -*- 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 ***** */
package org.mozilla.gecko.gfx;
import android.graphics.Point;
import android.graphics.Rect;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.LayerController;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
/**
* ViewportMetrics manages state and contains some utility functions related to
* the page viewport for the Gecko layer client to use.
*/
public class ViewportMetrics {
private IntSize mPageSize;
private Rect mViewportRect;
private Point mViewportOffset;
public ViewportMetrics() {
mPageSize = new IntSize(LayerController.TILE_WIDTH,
LayerController.TILE_HEIGHT);
mViewportRect = new Rect(0, 0, 1, 1);
mViewportOffset = new Point(0, 0);
}
public ViewportMetrics(ViewportMetrics viewport) {
mPageSize = new IntSize(viewport.getPageSize());
mViewportRect = new Rect(viewport.getViewport());
mViewportOffset = new Point(viewport.getViewportOffset());
}
public ViewportMetrics(JSONObject json) throws JSONException {
int x = json.getInt("x");
int y = json.getInt("y");
int width = json.getInt("width");
int height = json.getInt("height");
int pageWidth = json.getInt("pageWidth");
int pageHeight = json.getInt("pageHeight");
int offsetX = json.getInt("offsetX");
int offsetY = json.getInt("offsetY");
mPageSize = new IntSize(pageWidth, pageHeight);
mViewportRect = new Rect(x, y, x + width, y + height);
mViewportOffset = new Point(offsetX, offsetY);
}
public Point getOptimumViewportOffset() {
// XXX We currently always position the viewport in the centre of the
// displayport, but we might want to optimise this during panning
// to minimise checkerboarding.
Point optimumOffset =
new Point((LayerController.TILE_WIDTH - mViewportRect.width()) / 2,
(LayerController.TILE_HEIGHT - mViewportRect.height()) / 2);
/* XXX Until bug #524925 is fixed, changing the viewport origin will
* probably cause things to be slower than just having a smaller usable
* displayport.
*/
Rect viewport = getClampedViewport();
// Make sure this offset won't cause wasted pixels in the displayport
// (i.e. make sure the resultant displayport intersects with the page
// as much as possible)
if (viewport.left - optimumOffset.x < 0)
optimumOffset.x = viewport.left;
else if (optimumOffset.x + viewport.right > mPageSize.width)
optimumOffset.x -= (mPageSize.width - (optimumOffset.x + viewport.right));
if (viewport.top - optimumOffset.y < 0)
optimumOffset.y = viewport.top;
else if (optimumOffset.y + viewport.bottom > mPageSize.height)
optimumOffset.y -= (mPageSize.height - (optimumOffset.y + viewport.bottom));
return optimumOffset;
}
public Point getOrigin() {
return new Point(mViewportRect.left, mViewportRect.top);
}
public Point getDisplayportOrigin() {
return new Point(mViewportRect.left - mViewportOffset.x,
mViewportRect.top - mViewportOffset.y);
}
public IntSize getSize() {
return new IntSize(mViewportRect.width(), mViewportRect.height());
}
public Rect getViewport() {
return mViewportRect;
}
/** Returns the viewport rectangle, clamped within the page-size. */
public Rect getClampedViewport() {
Rect clampedViewport = new Rect(mViewportRect);
// While the viewport size ought to never exceed the page size, we
// do the clamping in this order to make sure that the origin is
// never negative.
if (clampedViewport.right > mPageSize.width)
clampedViewport.offset(mPageSize.width - clampedViewport.right, 0);
if (clampedViewport.left < 0)
clampedViewport.offset(-clampedViewport.left, 0);
if (clampedViewport.bottom > mPageSize.height)
clampedViewport.offset(0, mPageSize.height - clampedViewport.bottom);
if (clampedViewport.top < 0)
clampedViewport.offset(0, -clampedViewport.top);
return clampedViewport;
}
public Point getViewportOffset() {
return mViewportOffset;
}
public IntSize getPageSize() {
return mPageSize;
}
public void setPageSize(IntSize pageSize) {
mPageSize = pageSize;
}
public void setViewport(Rect viewport) {
mViewportRect = viewport;
}
public void setOrigin(Point origin) {
mViewportRect.set(origin.x, origin.y,
origin.x + mViewportRect.width(),
origin.y + mViewportRect.height());
}
public void setSize(IntSize size) {
mViewportRect.right = mViewportRect.left + size.width;
mViewportRect.bottom = mViewportRect.top + size.height;
}
public void setViewportOffset(Point offset) {
mViewportOffset = offset;
}
public boolean equals(ViewportMetrics viewport) {
return mViewportRect.equals(viewport.getViewport()) &&
mPageSize.equals(viewport.getPageSize()) &&
mViewportOffset.equals(viewport.getViewportOffset());
}
public String toJSON() {
return "{ \"x\" : " + mViewportRect.left +
", \"y\" : " + mViewportRect.top +
", \"width\" : " + mViewportRect.width() +
", \"height\" : " + mViewportRect.height() +
", \"pageWidth\" : " + mPageSize.width +
", \"pageHeight\" : " + mPageSize.height +
", \"offsetX\" : " + mViewportOffset.x +
", \"offsetY\" : " + mViewportOffset.y +
"}";
}
}

Просмотреть файл

@ -44,11 +44,13 @@ import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import java.lang.Math;
import java.util.Timer;
import java.util.TimerTask;
@ -266,7 +268,7 @@ public class PanZoomController
}
}
float zoomFactor = mController.getZoomFactor();
float zoomFactor = 1.0f;//mController.getZoomFactor();
mX.velocity = (mX.touchPos - x) / zoomFactor;
mY.velocity = (mY.touchPos - y) / zoomFactor;
mX.touchPos = x;
@ -315,15 +317,13 @@ public class PanZoomController
}
private void updatePosition() {
mController.scrollTo(mX.viewportPos, mY.viewportPos);
mController.notifyLayerClientOfGeometryChange();
mController.scrollTo(new PointF(mX.viewportPos, mY.viewportPos));
}
// Populates the viewport info and length in the axes.
private void populatePositionAndLength() {
IntSize pageSize = mController.getPageSize();
RectF visibleRect = mController.getVisibleRect();
IntSize screenSize = mController.getScreenSize();
RectF visibleRect = new RectF(mController.getViewport());
mX.setPageLength(pageSize.width);
mX.viewportPos = visibleRect.left;
@ -603,6 +603,7 @@ public class PanZoomController
*/
@Override
public boolean onScale(ScaleGestureDetector detector) {
/*
mState = PanZoomState.PINCHING;
float newZoom = detector.getCurrentSpan() / mInitialZoomSpan;
@ -614,11 +615,13 @@ public class PanZoomController
mController.setVisibleRect(x, y, width, height);
mController.notifyLayerClientOfGeometryChange();
populatePositionAndLength();
*/
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
/*
mState = PanZoomState.PINCHING;
RectF initialZoomRect = mController.getVisibleRect();
float initialZoom = mController.getZoomFactor();
@ -628,16 +631,19 @@ public class PanZoomController
mInitialZoomSpan = detector.getCurrentSpan() / initialZoom;
GeckoApp.mAppContext.hidePluginViews();
*/
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
/*
mState = PanZoomState.PANNING_HOLD_LOCKED;
mX.firstTouchPos = mX.touchPos = detector.getFocusX();
mY.firstTouchPos = mY.touchPos = detector.getFocusY();
GeckoApp.mAppContext.showPluginViews();
*/
}
@Override

Просмотреть файл

@ -1,110 +0,0 @@
/* -*- 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>
*
* 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 ***** */
package org.mozilla.gecko.ui;
import android.graphics.PointF;
import android.graphics.RectF;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.LayerController;
import org.mozilla.gecko.gfx.RectUtils;
/** Manages the dimensions of the page viewport. */
public class ViewportController {
private IntSize mPageSize;
private RectF mVisibleRect;
public ViewportController(IntSize pageSize, RectF visibleRect) {
mPageSize = pageSize;
mVisibleRect = visibleRect;
}
private float clamp(float min, float value, float max) {
if (max < min)
return min;
return (value < min) ? min : (value > max) ? max : value;
}
/** Returns the given rect, clamped to the boundaries of a tile. */
public RectF clampRect(RectF rect) {
float x = clamp(0, rect.left, mPageSize.width - LayerController.TILE_WIDTH);
float y = clamp(0, rect.top, mPageSize.height - LayerController.TILE_HEIGHT);
return new RectF(x, y, x + rect.width(), x + rect.height());
}
/** Returns the coordinates of a tile centered on the given rect. */
public static RectF widenRect(RectF rect) {
return new RectF(rect.centerX() - LayerController.TILE_WIDTH / 2,
rect.centerY() - LayerController.TILE_HEIGHT / 2,
rect.centerX() + LayerController.TILE_WIDTH / 2,
rect.centerY() + LayerController.TILE_HEIGHT / 2);
}
/**
* Given the layer controller's visible rect, page size, and screen size, returns the zoom
* factor.
*/
public float getZoomFactor(RectF layerVisibleRect, IntSize layerPageSize,
IntSize screenSize) {
RectF transformed = transformVisibleRect(layerVisibleRect, layerPageSize);
return (float)screenSize.width / transformed.width();
}
/**
* Given the visible rectangle that the user is viewing and the layer controller's page size,
* returns the dimensions of the box that this corresponds to on the page.
*/
public RectF transformVisibleRect(RectF layerVisibleRect, IntSize layerPageSize) {
float zoomFactor = (float)layerPageSize.width / (float)mPageSize.width;
return RectUtils.scale(layerVisibleRect, 1.0f / zoomFactor);
}
/**
* Given the visible rectangle that the user is viewing and the layer controller's page size,
* returns the dimensions in layer coordinates that this corresponds to.
*/
public RectF untransformVisibleRect(RectF viewportVisibleRect, IntSize layerPageSize) {
float zoomFactor = (float)layerPageSize.width / (float)mPageSize.width;
return RectUtils.scale(viewportVisibleRect, zoomFactor);
}
public IntSize getPageSize() { return mPageSize; }
public void setPageSize(IntSize pageSize) { mPageSize = pageSize; }
public RectF getVisibleRect() { return mVisibleRect; }
public void setVisibleRect(RectF visibleRect) { mVisibleRect = visibleRect; }
}

Просмотреть файл

@ -265,7 +265,7 @@ var BrowserApp = {
if (!aTab)
return;
this.deck.selectedPanel = aTab.browser;
this.deck.selectedPanel = aTab.vbox;
},
get selectedBrowser() {
@ -507,15 +507,7 @@ var BrowserApp = {
},
getDrawMetadata: function getDrawMetadata() {
return JSON.stringify(this.selectedTab.viewportMetrics);
},
setViewport: function setViewport(aNewViewport) {
this.selectedTab.viewportMetrics = aNewViewport;
/* TODO: Translate at edges. */
/* TODO: Zoom. */
this.selectedBrowser.contentWindow.scrollTo(aNewViewport.x, aNewViewport.y);
return JSON.stringify(this.selectedTab.viewport);
},
observe: function(aSubject, aTopic, aData) {
@ -557,7 +549,7 @@ var BrowserApp = {
} else if (aTopic == "FullScreen:Exit") {
browser.contentDocument.mozCancelFullScreen();
} else if (aTopic == "Viewport:Change") {
this.setViewport(JSON.parse(aData));
this.selectedTab.viewport = JSON.parse(aData);
}
}
}
@ -905,9 +897,11 @@ let gTabIDFactory = 0;
function Tab(aURL, aParams) {
this.browser = null;
this.vbox = null;
this.id = 0;
this.create(aURL, aParams);
this.viewportMetrics = { x: 0, y: 0 };
this._viewport = { x: 0, y: 0, width: 1, height: 1, offsetX: 0, offsetY: 0,
pageWidth: 1, pageHeight: 1 };
}
Tab.prototype = {
@ -915,12 +909,17 @@ Tab.prototype = {
if (this.browser)
return;
this.vbox = document.createElement("vbox");
this.vbox.align = "start";
BrowserApp.deck.appendChild(this.vbox);
this.browser = document.createElement("browser");
this.browser.setAttribute("type", "content");
this.browser.setAttribute("width", "980");
this.browser.setAttribute("height", "480");
BrowserApp.deck.appendChild(this.browser);
this.browser.style.width = "980px";
this.browser.style.height = "480px";
this.vbox.appendChild(this.browser);
// Turn off clipping so we can buffer areas outside of the browser element.
let frameLoader = this.browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
frameLoader.clipSubdocument = false;
@ -951,8 +950,9 @@ Tab.prototype = {
return;
this.browser.removeProgressListener(this);
BrowserApp.deck.removeChild(this.browser);
BrowserApp.deck.removeChild(this.vbox);
this.browser = null;
this.vbox = null;
let message = {
gecko: {
type: "Tab:Closed",
@ -982,6 +982,58 @@ Tab.prototype = {
return this.browser.getAttribute("type") == "content-primary";
},
set viewport(aViewport) {
// TODO: Zoom?
// Set scroll position
this.browser.contentWindow.scrollTo(aViewport.x, aViewport.y);
// Check if the viewport size/position has changed and set the necessary
// attributes on the browser element.
if (aViewport.width != this._viewport.width) {
this._viewport.width = aViewport.width;
this.browser.style.width = this._viewport.width.toString() + "px";
}
if (aViewport.height != this._viewport.height) {
this._viewport.height = aViewport.height;
this.browser.style.height = this._viewport.height.toString() + "px";
}
let transformChanged = false;
if (aViewport.offsetX != this._viewport.offsetX) {
this._viewport.offsetX = aViewport.offsetX;
transformChanged = true;
}
if (aViewport.offsetY != this._viewport.offsetY) {
this._viewport.offsetY = aViewport.offsetY;
transformChanged = true;
}
if (transformChanged)
this.browser.style.MozTransform =
"translate(" + this._viewport.offsetX + "px, " +
this._viewport.offsetY + "px)";
},
get viewport() {
// Update the viewport to current dimensions
this._viewport.x = this.browser.contentWindow.scrollX;
this._viewport.y = this.browser.contentWindow.scrollY;
let doc = this.browser.contentDocument.documentElement;
if (doc != null) {
this._viewport.pageWidth = doc.scrollWidth;
this._viewport.pageHeight = doc.scrollHeight;
} else {
this._viewport.pageWidth = this._viewport.width;
this._viewport.pageHeight = this._viewport.height;
}
return this._viewport;
},
onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) {
// Filter optimization: Only really send DOCUMENT state changes to Java listener
@ -1111,7 +1163,6 @@ var BrowserEventHandler = {
BrowserApp.deck.addEventListener("DOMLinkAdded", this, true);
BrowserApp.deck.addEventListener("DOMTitleChanged", this, true);
BrowserApp.deck.addEventListener("DOMUpdatePageReport", PopupBlockerObserver.onUpdatePageReport, false);
BrowserApp.deck.addEventListener("MozScrolledAreaChanged", this, true);
},
handleEvent: function(aEvent) {
@ -1122,13 +1173,20 @@ var BrowserEventHandler = {
return;
let tabID = BrowserApp.getTabForBrowser(browser).id;
let zoom = browser._zoom || 1;
let html = browser.contentDocument.documentElement;
let size = { width: html.scrollWidth * zoom, height: html.scrollHeight * zoom };
dump("### Sending pageSize: " + size.toSource());
sendMessageToJava({
gecko: {
type: "DOMContentLoaded",
tabID: tabID,
windowID: 0,
uri: browser.currentURI.spec,
title: browser.contentTitle
title: browser.contentTitle,
pageSize: size
}
});
@ -1489,19 +1547,6 @@ var BrowserEventHandler = {
window.mozRequestAnimationFrame(callback);
}
break;
case "MozScrolledAreaChanged":
let browser = BrowserApp.getBrowserForDocument(aEvent.target);
if (browser != BrowserApp.selectedBrowser)
return;
sendMessageToJava({
gecko: {
type: "PanZoom:Resize",
size: { width: aEvent.width, height: aEvent.height }
}
});
break;
}
},