| 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..e2263b48edb091ab100dacce5ea38aff83d8c202
|
| --- /dev/null
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/input/PopupTouchHandleDrawable.java
|
| @@ -0,0 +1,295 @@
|
| +// 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.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;
|
| +
|
| +import java.lang.ref.WeakReference;
|
| +
|
| +/**
|
| + * 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 PopupWindow mContainer;
|
| + private final Context mContext;
|
| + private final PositionObserver.Listener mParentPositionListener;
|
| +
|
| + // The weak delegate reference allows the PopupTouchHandleDrawable to be owned by a native
|
| + // object that might have a different lifetime (or a cyclic lifetime) with respect to the
|
| + // delegate, allowing garbage collection of any Java references.
|
| + private final WeakReference<PopupTouchHandleDrawableDelegate> mDelegate;
|
| +
|
| + // The observer reference will only be non-null while it is attached to mParentPositionListener.
|
| + private PositionObserver mParentPositionObserver;
|
| +
|
| + // 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 mAlpha;
|
| +
|
| + 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;
|
| +
|
| + /**
|
| + * Provides additional interaction behaviors necessary for handle
|
| + * manipulation and interaction.
|
| + */
|
| + public interface PopupTouchHandleDrawableDelegate {
|
| + /**
|
| + * @return The parent View of the PopupWindow.
|
| + */
|
| + View getParent();
|
| +
|
| + /**
|
| + * @return A position observer for the parent View, used to keep the
|
| + * absolutely positioned PopupWindow in-sync with the parent.
|
| + */
|
| + PositionObserver getParentPositionObserver();
|
| +
|
| + /**
|
| + * Should route MotionEvents to the appropriate logic layer for
|
| + * performing handle manipulation.
|
| + */
|
| + boolean onTouchHandleEvent(MotionEvent ev);
|
| + }
|
| +
|
| + public PopupTouchHandleDrawable(PopupTouchHandleDrawableDelegate delegate) {
|
| + super(delegate.getParent().getContext());
|
| + mContext = delegate.getParent().getContext();
|
| + mDelegate = new WeakReference<PopupTouchHandleDrawableDelegate>(delegate);
|
| + mContainer = new PopupWindow(mContext, 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);
|
| + }
|
| + };
|
| + }
|
| +
|
| + @Override
|
| + public boolean onTouchEvent(MotionEvent event) {
|
| + final PopupTouchHandleDrawableDelegate delegate = mDelegate.get();
|
| + if (delegate == null) {
|
| + // If the delegate is gone, we should immediately dispose of the popup.
|
| + hide();
|
| + return false;
|
| + }
|
| +
|
| + // Convert from PopupWindow local coordinates to
|
| + // parent view local coordinates prior to forwarding.
|
| + delegate.getParent().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 = delegate.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();
|
| +
|
| + switch (orientation) {
|
| + case LEFT: {
|
| + mDrawable = HandleViewResources.getLeftHandleDrawable(mContext);
|
| + mHotspotX = (mDrawable.getIntrinsicWidth() * 3) / 4f;
|
| + break;
|
| + }
|
| +
|
| + case RIGHT: {
|
| + mDrawable = HandleViewResources.getRightHandleDrawable(mContext);
|
| + mHotspotX = mDrawable.getIntrinsicWidth() / 4f;
|
| + break;
|
| + }
|
| +
|
| + case CENTER:
|
| + default: {
|
| + mDrawable = HandleViewResources.getCenterHandleDrawable(mContext);
|
| + mHotspotX = mDrawable.getIntrinsicWidth() / 2f;
|
| + break;
|
| + }
|
| + }
|
| + 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) {
|
| + 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) {
|
| + setMeasuredDimension(0, 0);
|
| + 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() {
|
| + if (mContainer.isShowing()) return;
|
| +
|
| + final PopupTouchHandleDrawableDelegate delegate = mDelegate.get();
|
| + if (delegate == null) {
|
| + hide();
|
| + return;
|
| + }
|
| +
|
| + mParentPositionObserver = delegate.getParentPositionObserver();
|
| + assert mParentPositionObserver != null;
|
| +
|
| + // 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(delegate.getParent(), 0,
|
| + getContainerPositionX(), getContainerPositionY());
|
| + }
|
| +
|
| + @CalledByNative
|
| + private void hide() {
|
| + mContainer.dismiss();
|
| + if (mParentPositionObserver != null) {
|
| + mParentPositionObserver.removeListener(mParentPositionListener);
|
| + // Clear the strong reference to allow garbage collection.
|
| + mParentPositionObserver = null;
|
| + }
|
| + }
|
| +
|
| + @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);
|
| + }
|
| +}
|
|
|