Chromium Code Reviews| Index: content/public/android/java/src/org/chromium/content/browser/InsertionHandleController.java |
| diff --git a/content/public/android/java/src/org/chromium/content/browser/InsertionHandleController.java b/content/public/android/java/src/org/chromium/content/browser/InsertionHandleController.java |
| index 8d84fded508d8aa45b404effe22efc0175542f45..50da3e93686274bbe50dc13b8093595ecf73d23e 100644 |
| --- a/content/public/android/java/src/org/chromium/content/browser/InsertionHandleController.java |
| +++ b/content/public/android/java/src/org/chromium/content/browser/InsertionHandleController.java |
| @@ -4,14 +4,35 @@ |
| package org.chromium.content.browser; |
| +import android.content.ClipboardManager; |
| import android.content.Context; |
| +import android.content.res.TypedArray; |
| +import android.graphics.drawable.Drawable; |
| +import android.view.Gravity; |
| +import android.view.LayoutInflater; |
| import android.view.View; |
| +import android.view.View.OnClickListener; |
| +import android.view.ViewGroup; |
| +import android.view.ViewGroup.LayoutParams; |
| +import android.widget.PopupWindow; |
| -class InsertionHandleController { |
| +/** |
| + * CursorController for inserting text at the cursor position. |
| + */ |
| +abstract public class InsertionHandleController implements CursorController { |
| + |
| + /** The handle view, lazily created when first shown */ |
| + private HandleView mHandle; |
| /** The view over which the insertion handle should be shown */ |
| private View mParent; |
| + /** True iff the insertion handle is currently showing */ |
| + private boolean mIsShowing; |
| + |
| + /** True iff the insertion handle can be shown automatically when selection changes */ |
| + private boolean mAllowAutomaticShowing; |
| + |
| private Context mContext; |
| InsertionHandleController(View parent) { |
| @@ -19,8 +40,251 @@ class InsertionHandleController { |
| mContext = parent.getContext(); |
| } |
| + /** Allows the handle to be shown automatically when cursor position changes */ |
| + public void allowAutomaticShowing() { |
|
bulach
2012/10/05 12:50:59
nit: as above, perhaps we don't need these to be p
Iain Merrick
2012/10/05 19:01:52
Done.
|
| + mAllowAutomaticShowing = true; |
| + } |
| + |
| /** Disallows the handle from being shown automatically when cursor position changes */ |
| public void hideAndDisallowAutomaticShowing() { |
| - // TODO(olilan): add method implementation (this is just a stub for ImeAdapter). |
| + hide(); |
| + mAllowAutomaticShowing = false; |
| + } |
| + |
| + /** |
| + * Sets the position and shows the handle. |
| + * @param x1 |
| + * @param y1 |
| + */ |
| + public void showHandleAt(int x, int y) { |
| + createHandleIfNeeded(); |
| + setHandlePosition(x, y); |
| + showHandleIfNeeded(); |
| + } |
| + |
| + public void showPastePopup() { |
| + if (mIsShowing) { |
| + mHandle.showPastePopupWindow(); |
| + } |
| + } |
| + |
| + public void showHandleWithPastePopupAt(int x, int y) { |
| + showHandleAt(x, y); |
| + showPastePopup(); |
| + } |
| + |
| + /** Shows the handle at the given coordinates, as long as automatic showing is allowed */ |
| + public void onCursorPositionChanged(int x, int y) { |
| + if (mAllowAutomaticShowing) { |
| + showHandleAt(x, y); |
| + } |
| + } |
| + |
| + /** |
| + * Moves the handle so that it points at the given coordinates. |
| + * @param x |
| + * @param y |
| + */ |
| + public void setHandlePosition(int x, int y) { |
| + mHandle.positionAt(x, y); |
| + } |
| + |
| + @Override |
| + public void onTouchModeChanged(boolean isInTouchMode) { |
| + if (!isInTouchMode) { |
| + hide(); |
| + } |
| + } |
| + |
| + @Override |
| + public void hide() { |
| + if (mIsShowing) { |
| + if (mHandle != null) mHandle.hide(); |
| + mIsShowing = false; |
| + } |
| + } |
| + |
| + @Override |
| + public boolean isShowing() { |
| + return mIsShowing; |
| + } |
| + |
| + @Override |
| + public void beforeStartUpdatingPosition(HandleView handle) {} |
| + |
| + @Override |
| + public void updatePosition(HandleView handle, int x, int y) { |
| + setCursorPosition(x, y); |
| + } |
| + |
| + /** |
| + * The concrete implementation must cause the cursor position to move to the given |
| + * coordinates and (possibly asynchronously) set the insertion handle position |
| + * after the cursor position change is made via showHandleAt(x,y). |
| + * @param x |
| + * @param y |
| + */ |
| + public abstract void setCursorPosition(int x, int y); |
| + |
| + /** Pastes the contents of clipboard at the current insertion point */ |
| + public abstract void paste(); |
| + |
| + /** Returns the current line height in pixels */ |
| + public abstract int getLineHeight(); |
| + |
| + @Override |
| + public void onDetached() {} |
| + |
| + public boolean canPaste() { |
| + return ((ClipboardManager)mContext.getSystemService( |
| + Context.CLIPBOARD_SERVICE)).hasPrimaryClip(); |
| + } |
| + |
| + private void createHandleIfNeeded() { |
| + if (mHandle == null) mHandle = new HandleView(this, HandleView.CENTER, mParent); |
| + } |
| + |
| + private void showHandleIfNeeded() { |
| + if (!mIsShowing) { |
| + mIsShowing = true; |
| + mHandle.show(); |
| + } |
| + } |
| + |
| + /* |
| + * This class is based on TextView.PastePopupMenu. |
| + */ |
| + class PastePopupMenu implements OnClickListener { |
| + private final PopupWindow mContainer; |
| + private int mPositionX; |
| + private int mPositionY; |
| + private View[] mPasteViews; |
| + private int[] mPasteViewLayouts; |
| + |
| + public PastePopupMenu() { |
| + mContainer = new PopupWindow(mContext, null, |
| + android.R.attr.textSelectHandleWindowStyle); |
| + mContainer.setSplitTouchEnabled(true); |
| + mContainer.setClippingEnabled(false); |
| + |
| + mContainer.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT); |
| + mContainer.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); |
| + |
| + final int[] POPUP_LAYOUT_ATTRS = { |
| + android.R.attr.textEditPasteWindowLayout, |
| + android.R.attr.textEditNoPasteWindowLayout, |
| + android.R.attr.textEditSidePasteWindowLayout, |
| + android.R.attr.textEditSideNoPasteWindowLayout, |
| + }; |
| + |
| + mPasteViews = new View[POPUP_LAYOUT_ATTRS.length]; |
| + mPasteViewLayouts = new int[POPUP_LAYOUT_ATTRS.length]; |
| + |
| + TypedArray attrs = mContext.obtainStyledAttributes(POPUP_LAYOUT_ATTRS); |
| + for (int i = 0; i < attrs.length(); ++i) { |
| + mPasteViewLayouts[i] = attrs.getResourceId(attrs.getIndex(i), 0); |
| + } |
| + attrs.recycle(); |
| + } |
| + |
| + private int viewIndex(boolean onTop) { |
| + return (onTop ? 0 : 1<<1) + (canPaste() ? 0 : 1 << 0); |
| + } |
| + |
| + private void updateContent(boolean onTop) { |
| + final int viewIndex = viewIndex(onTop); |
| + View view = mPasteViews[viewIndex]; |
| + |
| + if (view == null) { |
| + final int layout = mPasteViewLayouts[viewIndex]; |
| + LayoutInflater inflater = (LayoutInflater)mContext. |
| + getSystemService(Context.LAYOUT_INFLATER_SERVICE); |
| + if (inflater != null) { |
| + view = inflater.inflate(layout, null); |
| + } |
| + |
| + if (view == null) { |
| + throw new IllegalArgumentException("Unable to inflate TextEdit paste window"); |
| + } |
| + |
| + final int size = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); |
| + view.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, |
| + ViewGroup.LayoutParams.WRAP_CONTENT)); |
| + view.measure(size, size); |
| + |
| + view.setOnClickListener(this); |
| + |
| + mPasteViews[viewIndex] = view; |
| + } |
| + |
| + mContainer.setContentView(view); |
| + } |
| + |
| + public void show() { |
| + updateContent(true); |
| + positionAtCursor(); |
| + } |
| + |
| + public void hide() { |
| + mContainer.dismiss(); |
| + } |
| + |
| + public boolean isShowing() { |
| + return mContainer.isShowing(); |
| + } |
| + |
| + @Override |
| + public void onClick(View v) { |
| + if (canPaste()) { |
| + paste(); |
| + } |
| + hide(); |
| + } |
| + |
| + void positionAtCursor() { |
| + View contentView = mContainer.getContentView(); |
| + int width = contentView.getMeasuredWidth(); |
| + int height = contentView.getMeasuredHeight(); |
| + |
| + int lineHeight = getLineHeight(); |
| + |
| + mPositionX = (int) (mHandle.getAdjustedPositionX() - width / 2.0f); |
| + mPositionY = mHandle.getAdjustedPositionY() - height - lineHeight; |
| + |
| + final int[] coords = new int[2]; |
| + mParent.getLocationInWindow(coords); |
| + coords[0] += mPositionX; |
| + coords[1] += mPositionY; |
| + |
| + final int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels; |
| + if (coords[1] < 0) { |
| + updateContent(false); |
| + // Update dimensions from new view |
| + contentView = mContainer.getContentView(); |
| + width = contentView.getMeasuredWidth(); |
| + height = contentView.getMeasuredHeight(); |
| + |
| + // Vertical clipping, move under edited line and to the side of insertion cursor |
| + // TODO bottom clipping in case there is no system bar |
| + coords[1] += height; |
| + coords[1] += lineHeight; |
| + |
| + // Move to right hand side of insertion cursor by default. TODO RTL text. |
| + final Drawable handle = mHandle.getDrawable(); |
| + final int handleHalfWidth = handle.getIntrinsicWidth() / 2; |
| + |
| + if (mHandle.getAdjustedPositionX() + width < screenWidth) { |
| + coords[0] += handleHalfWidth + width / 2; |
| + } else { |
| + coords[0] -= handleHalfWidth + width / 2; |
| + } |
| + } else { |
| + // Horizontal clipping |
| + coords[0] = Math.max(0, coords[0]); |
| + coords[0] = Math.min(screenWidth - width, coords[0]); |
| + } |
| + |
| + mContainer.showAtLocation(mParent, Gravity.NO_GRAVITY, coords[0], coords[1]); |
| + } |
| } |
| } |