| Index: content/public/android/java/src/org/chromium/content/browser/input/HandleView.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/input/HandleView.java b/content/public/android/java/src/org/chromium/content/browser/input/HandleView.java
|
| index 88a6deaefea4fe0c835e75c433730ccf3a24636d..32075668b5166bdef76d43d18a79330d9ff4ad40 100644
|
| --- a/content/public/android/java/src/org/chromium/content/browser/input/HandleView.java
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/input/HandleView.java
|
| @@ -24,33 +24,44 @@ import android.view.ViewGroup.LayoutParams;
|
| import android.widget.PopupWindow;
|
| import android.widget.TextView;
|
|
|
| +import org.chromium.content.browser.PositionObserverInterface;
|
| +
|
| /**
|
| * View that displays a selection or insertion handle for text editing.
|
| + *
|
| + * While a HandleView is logically a child of some other view, it does not exist in that View's
|
| + * hierarchy.
|
| + *
|
| */
|
| public class HandleView extends View {
|
| private static final float FADE_DURATION = 200.f;
|
|
|
| private Drawable mDrawable;
|
| - private final PopupWindow mContainer;
|
| +
|
| + // The position of the handle relative to the parent view.
|
| private int mPositionX;
|
| private int mPositionY;
|
| +
|
| + // The position of the parent relative to the application's root view.
|
| + private int mParentPositionX;
|
| + private int mParentPositionY;
|
| +
|
| + // The offset from this handles position to the "tip" of the handle.
|
| + private float mHotspotX;
|
| + private float mHotspotY;
|
| +
|
| private final CursorController mController;
|
| private boolean mIsDragging;
|
| private float mTouchToWindowOffsetX;
|
| private float mTouchToWindowOffsetY;
|
| - private float mHotspotX;
|
| - private float mHotspotY;
|
| +
|
| private int mLineOffsetY;
|
| - private int mLastParentX;
|
| - private int mLastParentY;
|
| private float mDownPositionX, mDownPositionY;
|
| - private int mContainerPositionX, mContainerPositionY;
|
| private long mTouchTimer;
|
| private boolean mIsInsertionHandle = false;
|
| private float mAlpha;
|
| private long mFadeStartTime;
|
|
|
| - private View mParent;
|
| private InsertionHandleController.PastePopupMenu mPastePopupWindow;
|
|
|
| private final int mTextSelectHandleLeftRes;
|
| @@ -61,13 +72,14 @@ public class HandleView extends View {
|
| private Drawable mSelectHandleRight;
|
| private Drawable mSelectHandleCenter;
|
|
|
| - private final int[] mTempCoords = new int[2];
|
| - private final Rect mTempRect = new Rect();
|
| -
|
| static final int LEFT = 0;
|
| static final int CENTER = 1;
|
| static final int RIGHT = 2;
|
|
|
| + private Delegate mParent;
|
| + private PositionObserverInterface mParentPositionObserver;
|
| + private PositionObserverInterface.Listener mParentPositionListener;
|
| +
|
| // Number of dips to subtract from the handle's y position to give a suitable
|
| // y coordinate for the corresponding text position. This is to compensate for the fact
|
| // that the handle position is at the base of the line of text.
|
| @@ -79,16 +91,104 @@ public class HandleView extends View {
|
| android.R.attr.textSelectHandleRight,
|
| };
|
|
|
| - HandleView(CursorController controller, int pos, View parent) {
|
| + public interface Delegate {
|
| + /**
|
| + * @return The application context.
|
| + */
|
| + public Context getContext();
|
| +
|
| + /**
|
| + * @return Whether this position (relative to the root view) is visible in the handle's
|
| + * parent.
|
| + */
|
| + public boolean isPositionVisible(int x, int y);
|
| +
|
| + /**
|
| + * Dismiss the container containing the handle.
|
| + */
|
| + public void dismissContainer();
|
| +
|
| + /**
|
| + * @return Whether the container is showing.
|
| + */
|
| + public boolean isContainerShowing();
|
| +
|
| + /**
|
| + * Show the container for this handle.
|
| + *
|
| + * @param view The view to show in the container.
|
| + * @param x The x position (relative to the root view) to show the container at.
|
| + * @param y The y position (relative to the root view) to show the container at.
|
| + */
|
| + public void showContainerAtPosition(View view, int x, int y);
|
| +
|
| + /**
|
| + * Set the position and size of the container.
|
| + */
|
| + public void updateContainerPosition(int x, int y, int width, int height);
|
| + }
|
| +
|
| + /**
|
| + * A delegate for displaying a HandleView in a PopupWindow.
|
| + */
|
| + public static class ViewDelegate implements Delegate {
|
| + private final View mView;
|
| + private final PopupWindow mContainer;
|
| + private final Rect mTempRect = new Rect();
|
| +
|
| + public ViewDelegate(View view) {
|
| + mView = view;
|
| + mContainer = new PopupWindow(getContext(), null,
|
| + android.R.attr.textSelectHandleWindowStyle);
|
| + mContainer.setSplitTouchEnabled(true);
|
| + mContainer.setClippingEnabled(false);
|
| + }
|
| +
|
| + public Context getContext() {
|
| + return mView.getContext();
|
| + }
|
| +
|
| + public boolean isPositionVisible(int x, int y) {
|
| + final Rect clip = mTempRect;
|
| + clip.left = 0;
|
| + clip.top = 0;
|
| + clip.right = mView.getWidth();
|
| + clip.bottom = mView.getHeight();
|
| +
|
| + final ViewParent parent = mView.getParent();
|
| + if (parent == null || !parent.getChildVisibleRect(mView, clip, null)) {
|
| + return false;
|
| + }
|
| +
|
| + return x >= clip.left && x <= clip.right &&
|
| + y >= clip.top && y <= clip.bottom;
|
| + }
|
| +
|
| + public void dismissContainer() {
|
| + mContainer.dismiss();
|
| + }
|
| +
|
| + public boolean isContainerShowing() {
|
| + return mContainer.isShowing();
|
| + }
|
| +
|
| + public void showContainerAtPosition(View view, int x, int y) {
|
| + mContainer.setContentView(view);
|
| + mContainer.showAtLocation(mView, 0, x, y);
|
| + }
|
| +
|
| + public void updateContainerPosition(int x, int y, int width, int height) {
|
| + mContainer.update(x, y, width, height);
|
| + }
|
| + }
|
| +
|
| + HandleView(CursorController controller, int pos, Delegate parent,
|
| + PositionObserverInterface parentPositionObserver) {
|
| super(parent.getContext());
|
| - Context context = parent.getContext();
|
| mParent = parent;
|
| mController = controller;
|
| - mContainer = new PopupWindow(context, null, android.R.attr.textSelectHandleWindowStyle);
|
| - mContainer.setSplitTouchEnabled(true);
|
| - mContainer.setClippingEnabled(false);
|
|
|
| - TypedArray a = context.obtainStyledAttributes(TEXT_VIEW_HANDLE_ATTRS);
|
| + TypedArray a = mParent.getContext().obtainStyledAttributes(TEXT_VIEW_HANDLE_ATTRS);
|
| mTextSelectHandleLeftRes = a.getResourceId(a.getIndex(LEFT), 0);
|
| mTextSelectHandleRes = a.getResourceId(a.getIndex(CENTER), 0);
|
| mTextSelectHandleRightRes = a.getResourceId(a.getIndex(RIGHT), 0);
|
| @@ -98,9 +198,17 @@ public class HandleView extends View {
|
|
|
| // Convert line offset dips to pixels.
|
| mLineOffsetY = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
|
| - LINE_OFFSET_Y_DIP, context.getResources().getDisplayMetrics());
|
| + LINE_OFFSET_Y_DIP, mParent.getContext().getResources().getDisplayMetrics());
|
|
|
| mAlpha = 1.f;
|
| +
|
| + mParentPositionListener = new PositionObserverInterface.Listener() {
|
| + @Override
|
| + public void onPositionChanged(int x, int y) {
|
| + updateParentPosition(x, y);
|
| + }
|
| + };
|
| + mParentPositionObserver = parentPositionObserver;
|
| }
|
|
|
| void setOrientation(int pos) {
|
| @@ -152,21 +260,47 @@ public class HandleView extends View {
|
| mDrawable.getIntrinsicHeight());
|
| }
|
|
|
| - private void updateContainerPosition() {
|
| - final int[] coords = mTempCoords;
|
| - mParent.getLocationInWindow(coords);
|
| - mContainerPositionX = coords[0] + mPositionX;
|
| - mContainerPositionY = coords[1] + mPositionY;
|
| + private void updateParentPosition(int parentPositionX, int parentPositionY) {
|
| + // Hide paste popup window as soon as a scroll occurs.
|
| + if (mPastePopupWindow != null) {
|
| + mPastePopupWindow.hide();
|
| + }
|
| +
|
| + mTouchToWindowOffsetX += parentPositionX - mParentPositionX;
|
| + mTouchToWindowOffsetY += parentPositionY - mParentPositionY;
|
| + mParentPositionX = parentPositionX;
|
| + mParentPositionY = parentPositionY;
|
| + onPositionChanged();
|
| + }
|
| +
|
| + private int getContainerPositionX() {
|
| + return mParentPositionX + mPositionX;
|
| + }
|
| +
|
| + private int getContainerPositionY() {
|
| + return mParentPositionY + mPositionY;
|
| + }
|
| +
|
| + private void onPositionChanged() {
|
| + mParent.updateContainerPosition(getContainerPositionX(), getContainerPositionY(),
|
| + getRight() - getLeft(), getBottom() - getTop());
|
| + }
|
| +
|
| + private void showContainer() {
|
| + mParent.showContainerAtPosition(this, getContainerPositionX(), getContainerPositionY());
|
| }
|
|
|
| void show() {
|
| + // While hidden, the parent position may have become stale. It must be updated before
|
| + // checking isPositionVisible().
|
| + updateParentPosition(mParentPositionObserver.getPositionX(),
|
| + mParentPositionObserver.getPositionY());
|
| if (!isPositionVisible()) {
|
| hide();
|
| return;
|
| }
|
| - mContainer.setContentView(this);
|
| - updateContainerPosition();
|
| - mContainer.showAtLocation(mParent, 0, mContainerPositionX, mContainerPositionY);
|
| + mParentPositionObserver.addListener(mParentPositionListener);
|
| + showContainer();
|
|
|
| // Hide paste view when handle is moved on screen.
|
| if (mPastePopupWindow != null) {
|
| @@ -176,14 +310,15 @@ public class HandleView extends View {
|
|
|
| void hide() {
|
| mIsDragging = false;
|
| - mContainer.dismiss();
|
| + mParent.dismissContainer();
|
| + mParentPositionObserver.removeListener(mParentPositionListener);
|
| if (mPastePopupWindow != null) {
|
| mPastePopupWindow.hide();
|
| }
|
| }
|
|
|
| boolean isShowing() {
|
| - return mContainer.isShowing();
|
| + return mParent.isContainerShowing();
|
| }
|
|
|
| private boolean isPositionVisible() {
|
| @@ -192,66 +327,32 @@ public class HandleView extends View {
|
| return true;
|
| }
|
|
|
| - final Rect clip = mTempRect;
|
| - clip.left = 0;
|
| - clip.top = 0;
|
| - clip.right = mParent.getWidth();
|
| - clip.bottom = mParent.getHeight();
|
| -
|
| - final ViewParent parent = mParent.getParent();
|
| - if (parent == null || !parent.getChildVisibleRect(mParent, clip, null)) {
|
| - return false;
|
| - }
|
| -
|
| - final int[] coords = mTempCoords;
|
| - mParent.getLocationInWindow(coords);
|
| - final int posX = coords[0] + mPositionX + (int) mHotspotX;
|
| - final int posY = coords[1] + mPositionY + (int) mHotspotY;
|
| + final int posX = getRootViewRelativePositionX();
|
| + final int posY = getRootViewRelativePositionY();
|
|
|
| - return posX >= clip.left && posX <= clip.right &&
|
| - posY >= clip.top && posY <= clip.bottom;
|
| + return mParent.isPositionVisible(posX, posY);
|
| }
|
|
|
| // x and y are in physical pixels.
|
| void moveTo(int x, int y) {
|
| + int previousPositionX = mPositionX;
|
| + int previousPositionY = mPositionY;
|
| +
|
| mPositionX = x;
|
| mPositionY = y;
|
| if (isPositionVisible()) {
|
| - int[] coords = null;
|
| - if (mContainer.isShowing()) {
|
| - coords = mTempCoords;
|
| - mParent.getLocationInWindow(coords);
|
| - final int containerPositionX = coords[0] + mPositionX;
|
| - final int containerPositionY = coords[1] + mPositionY;
|
| -
|
| - if (containerPositionX != mContainerPositionX ||
|
| - containerPositionY != mContainerPositionY) {
|
| - mContainerPositionX = containerPositionX;
|
| - mContainerPositionY = containerPositionY;
|
| -
|
| - mContainer.update(mContainerPositionX, mContainerPositionY,
|
| - getRight() - getLeft(), getBottom() - getTop());
|
| -
|
| - // Hide paste popup window as soon as a scroll occurs.
|
| - if (mPastePopupWindow != null) {
|
| - mPastePopupWindow.hide();
|
| - }
|
| + if (mParent.isContainerShowing()) {
|
| + onPositionChanged();
|
| + // Hide paste popup window as soon as the handle is dragged.
|
| + if (mPastePopupWindow != null &&
|
| + (previousPositionX != mPositionX || previousPositionY != mPositionY)) {
|
| + mPastePopupWindow.hide();
|
| }
|
| } else {
|
| show();
|
| }
|
|
|
| if (mIsDragging) {
|
| - if (coords == null) {
|
| - coords = mTempCoords;
|
| - mParent.getLocationInWindow(coords);
|
| - }
|
| - if (coords[0] != mLastParentX || coords[1] != mLastParentY) {
|
| - mTouchToWindowOffsetX += coords[0] - mLastParentX;
|
| - mTouchToWindowOffsetY += coords[1] - mLastParentY;
|
| - mLastParentX = coords[0];
|
| - mLastParentY = coords[1];
|
| - }
|
| // Hide paste popup window as soon as the handle is dragged.
|
| if (mPastePopupWindow != null) {
|
| mPastePopupWindow.hide();
|
| @@ -265,9 +366,6 @@ public class HandleView extends View {
|
| @Override
|
| protected void onDraw(Canvas c) {
|
| updateAlpha();
|
| - updateContainerPosition();
|
| - mContainer.update(mContainerPositionX, mContainerPositionY,
|
| - getRight() - getLeft(), getBottom() - getTop());
|
| mDrawable.setBounds(0, 0, getRight() - getLeft(), getBottom() - getTop());
|
| mDrawable.draw(c);
|
| }
|
| @@ -280,10 +378,6 @@ public class HandleView extends View {
|
| mDownPositionY = ev.getRawY();
|
| mTouchToWindowOffsetX = mDownPositionX - mPositionX;
|
| mTouchToWindowOffsetY = mDownPositionY - mPositionY;
|
| - final int[] coords = mTempCoords;
|
| - mParent.getLocationInWindow(coords);
|
| - mLastParentX = coords[0];
|
| - mLastParentY = coords[1];
|
| mIsDragging = true;
|
| mController.beforeStartUpdatingPosition(this);
|
| mTouchTimer = SystemClock.uptimeMillis();
|
| @@ -347,17 +441,31 @@ public class HandleView extends View {
|
|
|
| // x and y are in physical pixels.
|
| void positionAt(int x, int y) {
|
| - moveTo((int)(x - mHotspotX), (int)(y - mHotspotY));
|
| + moveTo(x - Math.round(mHotspotX), y - Math.round(mHotspotY));
|
| }
|
|
|
| - // Returns the x coordinate of the position that the handle appears to be pointing to.
|
| + // Returns the x coordinate of the position that the handle appears to be pointing to relative
|
| + // to the handles "parent" view.
|
| int getAdjustedPositionX() {
|
| - return (int) (mPositionX + mHotspotX);
|
| + return mPositionX + Math.round(mHotspotX);
|
| }
|
|
|
| - // Returns the y coordinate of the position that the handle appears to be pointing to.
|
| + // Returns the y coordinate of the position that the handle appears to be pointing to relative
|
| + // to the handles "parent" view.
|
| int getAdjustedPositionY() {
|
| - return (int) (mPositionY + mHotspotY);
|
| + return mPositionY + Math.round(mHotspotY);
|
| + }
|
| +
|
| + // Returns the x coordinate of the postion that the handle appears to be pointing to relative to
|
| + // the root view of the application.
|
| + int getRootViewRelativePositionX() {
|
| + return getContainerPositionX() + Math.round(mHotspotX);
|
| + }
|
| +
|
| + // Returns the y coordinate of the postion that the handle appears to be pointing to relative to
|
| + // the root view of the application.
|
| + int getRootViewRelativePositionY() {
|
| + return getContainerPositionY() + Math.round(mHotspotY);
|
| }
|
|
|
| // Returns a suitable y coordinate for the text position corresponding to the handle.
|
|
|