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. |