Index: content/public/android/java/src/org/chromium/content/browser/input/PopupTouchHandleDrawable.java |
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/PopupTouchHandleDrawable.java b/content/public/android/java/src/org/chromium/content/browser/input/PopupTouchHandleDrawable.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..62a80080b27935a8e643c903939601dc51befd57 |
--- /dev/null |
+++ b/content/public/android/java/src/org/chromium/content/browser/input/PopupTouchHandleDrawable.java |
@@ -0,0 +1,265 @@ |
+// Copyright 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.content.browser.input; |
+ |
+import android.content.Context; |
+import android.graphics.Canvas; |
+import android.graphics.Rect; |
+import android.graphics.drawable.Drawable; |
+import android.view.MotionEvent; |
+import android.view.View; |
+import android.widget.PopupWindow; |
+ |
+import org.chromium.base.CalledByNative; |
+import org.chromium.base.JNINamespace; |
+import org.chromium.content.browser.PositionObserver; |
+ |
+/** |
+ * 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. |
+ * |
+ */ |
+@JNINamespace("content") |
+public class PopupTouchHandleDrawable extends View { |
+ private Drawable mDrawable; |
+ private final PopupTouchHandleDrawableDelegate mDelegate; |
+ 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 float mTouchToWindowOffsetX; |
+ private float mTouchToWindowOffsetY; |
+ |
+ private float mAlpha; |
+ |
+ private final View mParent; |
+ |
+ private final Rect mTempRect = new Rect(); |
+ private final int[] mTempScreenCoords = new int[2]; |
+ |
+ static final int LEFT = 0; |
+ static final int CENTER = 1; |
+ static final int RIGHT = 2; |
+ private int mOrientation = -1; |
+ |
+ private final PositionObserver mParentPositionObserver; |
+ private final PositionObserver.Listener mParentPositionListener; |
+ |
+ /** |
+ * Provides additional interaction behaviors necessary for handle |
+ * manipulation and interaction. |
+ */ |
+ public interface PopupTouchHandleDrawableDelegate { |
+ /** |
+ * Should route MotionEvents to the appropriate logic layer for |
+ * performing handle manipulation. |
+ */ |
+ boolean onTouchHandleEvent(MotionEvent ev); |
+ } |
+ |
+ public PopupTouchHandleDrawable(PopupTouchHandleDrawableDelegate delegate, View parent, |
+ PositionObserver parentPositionObserver) { |
+ super(parent.getContext()); |
+ mDelegate = delegate; |
+ mParent = parent; |
+ Context context = mParent.getContext(); |
+ mContainer = new PopupWindow(context, null, android.R.attr.textSelectHandleWindowStyle); |
+ mContainer.setSplitTouchEnabled(true); |
+ mContainer.setClippingEnabled(false); |
+ mAlpha = 1.f; |
+ |
+ mParentPositionListener = new PositionObserver.Listener() { |
+ @Override |
+ public void onPositionChanged(int x, int y) { |
+ updateParentPosition(x, y); |
+ } |
+ }; |
+ mParentPositionObserver = parentPositionObserver; |
+ } |
+ |
+ @Override |
+ public boolean onTouchEvent(MotionEvent event) { |
+ // Convert from PopupWindow local coordinates to |
+ // parent view local coordinates prior to forwarding. |
+ mParent.getLocationOnScreen(mTempScreenCoords); |
+ final float offsetX = event.getRawX() - event.getX() - mTempScreenCoords[0]; |
+ final float offsetY = event.getRawY() - event.getY() - mTempScreenCoords[1]; |
+ final MotionEvent offsetEvent = MotionEvent.obtainNoHistory(event); |
+ offsetEvent.offsetLocation(offsetX, offsetY); |
+ final boolean handled = mDelegate.onTouchHandleEvent(offsetEvent); |
+ offsetEvent.recycle(); |
+ return handled; |
+ } |
+ |
+ private void setOrientation(int orientation) { |
+ assert orientation >= LEFT && orientation <= RIGHT; |
+ if (mOrientation == orientation) return; |
+ |
+ final boolean hadValidOrientation = mOrientation != -1; |
+ mOrientation = orientation; |
+ |
+ final int oldAdjustedPositionX = getAdjustedPositionX(); |
+ final int oldAdjustedPositionY = getAdjustedPositionY(); |
+ |
+ Context context = mParent.getContext(); |
+ switch (orientation) { |
+ case LEFT: { |
+ mDrawable = HandleViewResources.getLeftHandleDrawable(context); |
+ mHotspotX = (mDrawable.getIntrinsicWidth() * 3) / 4f; |
+ break; |
+ } |
+ |
+ case RIGHT: { |
+ mDrawable = HandleViewResources.getRightHandleDrawable(context); |
+ mHotspotX = mDrawable.getIntrinsicWidth() / 4f; |
+ break; |
+ } |
+ |
+ case CENTER: |
+ default: { |
+ mDrawable = HandleViewResources.getCenterHandleDrawable(context); |
+ mHotspotX = mDrawable.getIntrinsicWidth() / 2f; |
+ break; |
+ } |
+ } |
+ assert mDrawable != null; |
+ mHotspotY = 0; |
+ |
+ // Force handle repositioning to accommodate the new orientation's hotspot. |
+ if (hadValidOrientation) positionAt(oldAdjustedPositionX, oldAdjustedPositionY); |
+ mDrawable.setAlpha((int) (255 * mAlpha)); |
+ |
+ invalidate(); |
+ } |
+ |
+ private void updateParentPosition(int parentPositionX, int parentPositionY) { |
+ 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() { |
+ mContainer.update(getContainerPositionX(), getContainerPositionY(), |
+ getRight() - getLeft(), getBottom() - getTop()); |
+ } |
+ |
+ // x and y are in physical pixels. |
+ private void moveTo(int x, int y) { |
+ mPositionX = x; |
+ mPositionY = y; |
+ onPositionChanged(); |
+ } |
+ |
+ @Override |
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
+ if (mDrawable == null) return; |
+ setMeasuredDimension(mDrawable.getIntrinsicWidth(), |
+ mDrawable.getIntrinsicHeight()); |
+ } |
+ |
+ @Override |
+ protected void onDraw(Canvas c) { |
+ if (mDrawable == null) return; |
+ mDrawable.setBounds(0, 0, getRight() - getLeft(), getBottom() - getTop()); |
+ mDrawable.draw(c); |
+ } |
+ |
+ // x and y are in physical pixels. |
+ private void positionAt(int x, int y) { |
+ moveTo(x - Math.round(mHotspotX), y - Math.round(mHotspotY)); |
+ } |
+ |
+ // Returns the x coordinate of the position that the handle appears to be pointing to relative |
+ // to the handles "parent" view. |
+ private int getAdjustedPositionX() { |
+ return mPositionX + Math.round(mHotspotX); |
+ } |
+ |
+ // Returns the y coordinate of the position that the handle appears to be pointing to relative |
+ // to the handles "parent" view. |
+ private int getAdjustedPositionY() { |
+ return mPositionY + Math.round(mHotspotY); |
+ } |
+ |
+ @CalledByNative |
+ private void show() { |
+ // While hidden, the parent position may have become stale. It must be updated before |
+ // checking isPositionVisible(). |
+ updateParentPosition(mParentPositionObserver.getPositionX(), |
+ mParentPositionObserver.getPositionY()); |
+ mParentPositionObserver.addListener(mParentPositionListener); |
+ mContainer.setContentView(this); |
+ mContainer.showAtLocation(mParent, 0, getContainerPositionX(), getContainerPositionY()); |
+ } |
+ |
+ @CalledByNative |
+ private void hide() { |
+ mContainer.dismiss(); |
+ mParentPositionObserver.removeListener(mParentPositionListener); |
+ } |
+ |
+ @CalledByNative |
+ private void setRightOrientation() { |
+ setOrientation(RIGHT); |
+ } |
+ |
+ @CalledByNative |
+ private void setLeftOrientation() { |
+ setOrientation(LEFT); |
+ } |
+ |
+ @CalledByNative |
+ private void setCenterOrientation() { |
+ setOrientation(CENTER); |
+ } |
+ |
+ @CalledByNative |
+ private void setOpacity(float alpha) { |
+ if (mAlpha == alpha) return; |
+ mAlpha = alpha; |
+ if (mDrawable != null) mDrawable.setAlpha((int) (255 * mAlpha)); |
+ } |
+ |
+ @CalledByNative |
+ private void setFocus(float x, float y) { |
+ positionAt((int) x, (int) y); |
+ } |
+ |
+ @CalledByNative |
+ private void setVisible(boolean visible) { |
+ setVisibility(visible ? VISIBLE : INVISIBLE); |
+ } |
+ |
+ @CalledByNative |
+ private boolean containsPoint(float x, float y) { |
+ if (mDrawable == null) return false; |
+ final int width = mDrawable.getIntrinsicWidth(); |
+ final int height = mDrawable.getIntrinsicHeight(); |
+ return x > mPositionX && x < (mPositionX + width) |
+ && y > mPositionY && y < (mPositionY + height); |
+ } |
+} |