Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2190)

Unified Diff: content/public/android/java/src/org/chromium/content/browser/input/PopupTouchHandleDrawable.java

Issue 335943002: [Android] Composited selection handle rendering (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@input_native_handles_final
Patch Set: Rebase Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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);
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698