diff --git a/mobile/android/base/TextSelection.java b/mobile/android/base/TextSelection.java index 810fee945145..622462db56c7 100644 --- a/mobile/android/base/TextSelection.java +++ b/mobile/android/base/TextSelection.java @@ -6,14 +6,21 @@ package org.mozilla.gecko; import android.util.Log; import android.view.View; +import org.mozilla.gecko.gfx.Layer; +import org.mozilla.gecko.gfx.Layer.RenderContext; +import org.mozilla.gecko.gfx.LayerController; import org.json.JSONObject; -class TextSelection implements GeckoEventListener { +class TextSelection extends Layer implements GeckoEventListener { private static final String LOGTAG = "GeckoTextSelection"; private final TextSelectionHandle mStartHandle; private final TextSelectionHandle mEndHandle; + private float mViewLeft; + private float mViewTop; + private float mViewZoom; + TextSelection(TextSelectionHandle startHandle, TextSelectionHandle endHandle) { mStartHandle = startHandle; mEndHandle = endHandle; @@ -41,11 +48,24 @@ class TextSelection implements GeckoEventListener { public void run() { mStartHandle.setVisibility(View.VISIBLE); mEndHandle.setVisibility(View.VISIBLE); + + mViewLeft = 0.0f; + mViewTop = 0.0f; + mViewZoom = 0.0f; + LayerController layerController = GeckoApp.mAppContext.getLayerController(); + if (layerController != null) { + layerController.getView().addLayer(TextSelection.this); + } } }); } else if (event.equals("TextSelection:HideHandles")) { GeckoApp.mAppContext.mMainHandler.post(new Runnable() { public void run() { + LayerController layerController = GeckoApp.mAppContext.getLayerController(); + if (layerController != null) { + layerController.getView().removeLayer(TextSelection.this); + } + mStartHandle.setVisibility(View.GONE); mEndHandle.setVisibility(View.GONE); } @@ -67,4 +87,26 @@ class TextSelection implements GeckoEventListener { Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); } } + + @Override + public void draw(final RenderContext context) { + // cache the relevant values from the context and bail out if they are the same. we do this + // because this draw function gets called a lot (once per compositor frame) and we want to + // avoid doing a lot of extra work in cases where it's not needed. + if (FloatUtils.fuzzyEquals(mViewLeft, context.viewport.left) + && FloatUtils.fuzzyEquals(mViewTop, context.viewport.top) + && FloatUtils.fuzzyEquals(mViewZoom, context.zoomFactor)) { + return; + } + mViewLeft = context.viewport.left; + mViewTop = context.viewport.top; + mViewZoom = context.zoomFactor; + + GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + public void run() { + mStartHandle.repositionWithViewport(context.viewport.left, context.viewport.top, context.zoomFactor); + mEndHandle.repositionWithViewport(context.viewport.left, context.viewport.top, context.zoomFactor); + } + }); + } } diff --git a/mobile/android/base/TextSelectionHandle.java b/mobile/android/base/TextSelectionHandle.java index 6d64d3501c42..6e8eedd6a4c6 100644 --- a/mobile/android/base/TextSelectionHandle.java +++ b/mobile/android/base/TextSelectionHandle.java @@ -13,6 +13,7 @@ import android.view.MotionEvent; import android.view.View; import android.widget.RelativeLayout; import android.widget.ImageView; +import org.mozilla.gecko.gfx.ImmutableViewportMetrics; import org.mozilla.gecko.gfx.LayerController; import org.json.JSONObject; @@ -28,6 +29,7 @@ class TextSelectionHandle extends ImageView implements View.OnTouchListener { private int mLeft; private int mTop; + private PointF mGeckoPoint; private int mTouchStartX; private int mTouchStartY; @@ -108,11 +110,18 @@ class TextSelectionHandle extends ImageView implements View.OnTouchListener { Log.e(LOGTAG, "Can't position handle because layerController is null"); return; } - PointF geckoPoint = new PointF((float) left, (float) top); - geckoPoint = layerController.convertLayerPointToViewPoint(geckoPoint); - mLeft = Math.round(geckoPoint.x) - (mHandleType.equals(HandleType.START) ? mWidth - mShadow : mShadow); - mTop = Math.round(geckoPoint.y); + mGeckoPoint = new PointF((float) left, (float) top); + ImmutableViewportMetrics metrics = layerController.getViewportMetrics(); + repositionWithViewport(metrics.viewportRectLeft, metrics.viewportRectTop, metrics.zoomFactor); + } + + void repositionWithViewport(float x, float y, float zoom) { + PointF viewPoint = new PointF((mGeckoPoint.x * zoom) - x, + (mGeckoPoint.y * zoom) - y); + + mLeft = Math.round(viewPoint.x) - (mHandleType.equals(HandleType.START) ? mWidth - mShadow : mShadow); + mTop = Math.round(viewPoint.y); setLayoutPosition(); } diff --git a/mobile/android/base/gfx/LayerController.java b/mobile/android/base/gfx/LayerController.java index a3b9020e770f..a6a6abc63a05 100644 --- a/mobile/android/base/gfx/LayerController.java +++ b/mobile/android/base/gfx/LayerController.java @@ -300,28 +300,6 @@ public class LayerController { return layerPoint; } - /** - * Does the opposite of convertViewPointToLayerPoint. - */ - public PointF convertLayerPointToViewPoint(PointF layerPoint) { - if (mLayerClient == null) { - return null; - } - - ImmutableViewportMetrics viewportMetrics = mViewportMetrics; - PointF origin = viewportMetrics.getOrigin(); - float zoom = viewportMetrics.zoomFactor; - ViewportMetrics geckoViewport = mLayerClient.getGeckoViewportMetrics(); - PointF geckoOrigin = geckoViewport.getOrigin(); - float geckoZoom = geckoViewport.getZoomFactor(); - - PointF viewPoint = new PointF( - ((layerPoint.x + (geckoOrigin.x / geckoZoom)) * zoom - origin.x), - ((layerPoint.y + (geckoOrigin.y / geckoZoom)) * zoom - origin.y)); - - return viewPoint; - } - /** Retrieves whether we should show checkerboard checks or not. */ public boolean checkerboardShouldShowChecks() { return mCheckerboardShouldShowChecks; diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index b7873c31e53d..9c13181719af 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -1565,10 +1565,8 @@ var SelectionHandler = { break; } case "after-viewport-change": { - // Update the cache and reposition the handles after the viewport - // changes (e.g. panning, zooming). + // Update the cache after the viewport changes (e.g. panning, zooming). this.updateCacheForSelection(); - this.positionHandles(); break; } case "TextSelection:Move": { @@ -1871,13 +1869,15 @@ var SelectionHandler = { // Translate coordinates to account for selections in sub-frames. We can't cache // this because the top-level page may have scrolled since selection started. let offset = this._getViewOffset(); + let scrollX = {}, scrollY = {}; + this._view.top.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).getScrollXY(false, scrollX, scrollY); sendMessageToJava({ gecko: { type: "TextSelection:PositionHandles", - startLeft: this.cache.start.x + offset.x, - startTop: this.cache.start.y + offset.y, - endLeft: this.cache.end.x + offset.x, - endTop: this.cache.end.y + offset.y + startLeft: this.cache.start.x + offset.x + scrollX.value, + startTop: this.cache.start.y + offset.y + scrollY.value, + endLeft: this.cache.end.x + offset.x + scrollX.value, + endTop: this.cache.end.y + offset.y + scrollY.value } }); },