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

Side by Side Diff: content/public/android/java/src/org/chromium/content/browser/input/InsertionHandleController.java

Issue 335943002: [Android] Composited selection handle rendering (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@input_native_handles_final
Patch Set: Fix animation tests 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.content.browser.input;
6
7 import android.content.ClipboardManager;
8 import android.content.Context;
9 import android.content.res.TypedArray;
10 import android.graphics.drawable.Drawable;
11 import android.view.Gravity;
12 import android.view.LayoutInflater;
13 import android.view.View;
14 import android.view.View.OnClickListener;
15 import android.view.ViewGroup;
16 import android.view.ViewGroup.LayoutParams;
17 import android.widget.PopupWindow;
18
19 import com.google.common.annotations.VisibleForTesting;
20
21 import org.chromium.content.browser.PositionObserver;
22
23 /**
24 * CursorController for inserting text at the cursor position.
25 */
26 public abstract class InsertionHandleController implements CursorController {
27
28 /** The handle view, lazily created when first shown */
29 private HandleView mHandle;
30
31 /** The view over which the insertion handle should be shown */
32 private final View mParent;
33
34 /** True iff the insertion handle is currently showing */
35 private boolean mIsShowing;
36
37 /** True iff the insertion handle can be shown automatically when selection changes */
38 private boolean mAllowAutomaticShowing;
39
40 private final Context mContext;
41
42 private final PositionObserver mPositionObserver;
43
44 public InsertionHandleController(View parent, PositionObserver positionObser ver) {
45 mParent = parent;
46
47 mContext = parent.getContext();
48 mPositionObserver = positionObserver;
49 }
50
51 /** Allows the handle to be shown automatically when cursor position changes */
52 public void allowAutomaticShowing() {
53 mAllowAutomaticShowing = true;
54 }
55
56 /** Disallows the handle from being shown automatically when cursor position changes */
57 public void hideAndDisallowAutomaticShowing() {
58 hide();
59 mAllowAutomaticShowing = false;
60 }
61
62 /**
63 * Shows the handle.
64 */
65 public void showHandle() {
66 createHandleIfNeeded();
67 showHandleIfNeeded();
68 }
69
70 void showPastePopup() {
71 if (mIsShowing) {
72 mHandle.showPastePopupWindow();
73 }
74 }
75
76 public void showHandleWithPastePopup() {
77 showHandle();
78 showPastePopup();
79 }
80
81 /**
82 * @return whether the handle is being dragged.
83 */
84 public boolean isDragging() {
85 return mHandle != null && mHandle.isDragging();
86 }
87
88 /** Shows the handle at the given coordinates, as long as automatic showing is allowed */
89 public void onCursorPositionChanged() {
90 if (mAllowAutomaticShowing) {
91 showHandle();
92 }
93 }
94
95 /**
96 * Moves the handle so that it points at the given coordinates.
97 * @param x Handle x in physical pixels.
98 * @param y Handle y in physical pixels.
99 */
100 public void setHandlePosition(float x, float y) {
101 mHandle.positionAt((int) x, (int) y);
102 }
103
104 /**
105 * If the handle is not visible, sets its visibility to View.VISIBLE and beg ins fading it in.
106 */
107 public void beginHandleFadeIn() {
108 mHandle.beginFadeIn();
109 }
110
111 /**
112 * Sets the handle to the given visibility.
113 */
114 public void setHandleVisibility(int visibility) {
115 mHandle.setVisibility(visibility);
116 }
117
118 int getHandleX() {
119 return mHandle.getAdjustedPositionX();
120 }
121
122 int getHandleY() {
123 return mHandle.getAdjustedPositionY();
124 }
125
126 @VisibleForTesting
127 public HandleView getHandleViewForTest() {
128 return mHandle;
129 }
130
131 @Override
132 public void onTouchModeChanged(boolean isInTouchMode) {
133 if (!isInTouchMode) {
134 hide();
135 }
136 }
137
138 @Override
139 public void hide() {
140 if (mIsShowing) {
141 if (mHandle != null) mHandle.hide();
142 mIsShowing = false;
143 }
144 }
145
146 @Override
147 public boolean isShowing() {
148 return mIsShowing;
149 }
150
151 @Override
152 public void beforeStartUpdatingPosition(HandleView handle) {}
153
154 @Override
155 public void updatePosition(HandleView handle, int x, int y) {
156 setCursorPosition(x, y);
157 }
158
159 /**
160 * The concrete implementation must cause the cursor position to move to the given
161 * coordinates and (possibly asynchronously) set the insertion handle positi on
162 * after the cursor position change is made via setHandlePosition.
163 * @param x
164 * @param y
165 */
166 protected abstract void setCursorPosition(int x, int y);
167
168 /** Pastes the contents of clipboard at the current insertion point */
169 protected abstract void paste();
170
171 /** Returns the current line height in pixels */
172 protected abstract int getLineHeight();
173
174 @Override
175 public void onDetached() {}
176
177 boolean canPaste() {
178 return ((ClipboardManager) mContext.getSystemService(
179 Context.CLIPBOARD_SERVICE)).hasPrimaryClip();
180 }
181
182 private void createHandleIfNeeded() {
183 if (mHandle == null) {
184 mHandle = new HandleView(this, HandleView.CENTER, mParent, mPosition Observer);
185 }
186 }
187
188 private void showHandleIfNeeded() {
189 if (!mIsShowing) {
190 mIsShowing = true;
191 mHandle.show();
192 setHandleVisibility(HandleView.VISIBLE);
193 }
194 }
195
196 /*
197 * This class is based on TextView.PastePopupMenu.
198 */
199 class PastePopupMenu implements OnClickListener {
200 private final PopupWindow mContainer;
201 private int mPositionX;
202 private int mPositionY;
203 private final View[] mPasteViews;
204 private final int[] mPasteViewLayouts;
205
206 public PastePopupMenu() {
207 mContainer = new PopupWindow(mContext, null,
208 android.R.attr.textSelectHandleWindowStyle);
209 mContainer.setSplitTouchEnabled(true);
210 mContainer.setClippingEnabled(false);
211
212 mContainer.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
213 mContainer.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
214
215 final int[] POPUP_LAYOUT_ATTRS = {
216 android.R.attr.textEditPasteWindowLayout,
217 android.R.attr.textEditNoPasteWindowLayout,
218 android.R.attr.textEditSidePasteWindowLayout,
219 android.R.attr.textEditSideNoPasteWindowLayout,
220 };
221
222 mPasteViews = new View[POPUP_LAYOUT_ATTRS.length];
223 mPasteViewLayouts = new int[POPUP_LAYOUT_ATTRS.length];
224
225 TypedArray attrs = mContext.obtainStyledAttributes(POPUP_LAYOUT_ATTR S);
226 for (int i = 0; i < attrs.length(); ++i) {
227 mPasteViewLayouts[i] = attrs.getResourceId(attrs.getIndex(i), 0) ;
228 }
229 attrs.recycle();
230 }
231
232 private int viewIndex(boolean onTop) {
233 return (onTop ? 0 : 1 << 1) + (canPaste() ? 0 : 1 << 0);
234 }
235
236 private void updateContent(boolean onTop) {
237 final int viewIndex = viewIndex(onTop);
238 View view = mPasteViews[viewIndex];
239
240 if (view == null) {
241 final int layout = mPasteViewLayouts[viewIndex];
242 LayoutInflater inflater = (LayoutInflater) mContext.
243 getSystemService(Context.LAYOUT_INFLATER_SERVICE);
244 if (inflater != null) {
245 view = inflater.inflate(layout, null);
246 }
247
248 if (view == null) {
249 throw new IllegalArgumentException("Unable to inflate TextEd it paste window");
250 }
251
252 final int size = View.MeasureSpec.makeMeasureSpec(0, View.Measur eSpec.UNSPECIFIED);
253 view.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRA P_CONTENT,
254 ViewGroup.LayoutParams.WRAP_CONTENT));
255 view.measure(size, size);
256
257 view.setOnClickListener(this);
258
259 mPasteViews[viewIndex] = view;
260 }
261
262 mContainer.setContentView(view);
263 }
264
265 void show() {
266 updateContent(true);
267 positionAtCursor();
268 }
269
270 void hide() {
271 mContainer.dismiss();
272 }
273
274 boolean isShowing() {
275 return mContainer.isShowing();
276 }
277
278 @Override
279 public void onClick(View v) {
280 if (canPaste()) {
281 paste();
282 }
283 hide();
284 }
285
286 void positionAtCursor() {
287 View contentView = mContainer.getContentView();
288 int width = contentView.getMeasuredWidth();
289 int height = contentView.getMeasuredHeight();
290
291 int lineHeight = getLineHeight();
292
293 mPositionX = (int) (mHandle.getAdjustedPositionX() - width / 2.0f);
294 mPositionY = mHandle.getAdjustedPositionY() - height - lineHeight;
295
296 final int[] coords = new int[2];
297 mParent.getLocationInWindow(coords);
298 coords[0] += mPositionX;
299 coords[1] += mPositionY;
300
301 final int screenWidth = mContext.getResources().getDisplayMetrics(). widthPixels;
302 if (coords[1] < 0) {
303 updateContent(false);
304 // Update dimensions from new view
305 contentView = mContainer.getContentView();
306 width = contentView.getMeasuredWidth();
307 height = contentView.getMeasuredHeight();
308
309 // Vertical clipping, move under edited line and to the side of insertion cursor
310 // TODO bottom clipping in case there is no system bar
311 coords[1] += height;
312 coords[1] += lineHeight;
313
314 // Move to right hand side of insertion cursor by default. TODO RTL text.
315 final Drawable handle = mHandle.getDrawable();
316 final int handleHalfWidth = handle.getIntrinsicWidth() / 2;
317
318 if (mHandle.getAdjustedPositionX() + width < screenWidth) {
319 coords[0] += handleHalfWidth + width / 2;
320 } else {
321 coords[0] -= handleHalfWidth + width / 2;
322 }
323 } else {
324 // Horizontal clipping
325 coords[0] = Math.max(0, coords[0]);
326 coords[0] = Math.min(screenWidth - width, coords[0]);
327 }
328
329 mContainer.showAtLocation(mParent, Gravity.NO_GRAVITY, coords[0], co ords[1]);
330 }
331 }
332 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698