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

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

Issue 24449007: [Android] Allow text handles to observe position of "parent" view (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address tedchoc's comments Created 7 years, 2 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.content.browser.input; 5 package org.chromium.content.browser.input;
6 6
7 import android.content.Context; 7 import android.content.Context;
8 import android.content.res.TypedArray; 8 import android.content.res.TypedArray;
9 import android.graphics.Canvas; 9 import android.graphics.Canvas;
10 import android.graphics.Rect; 10 import android.graphics.Rect;
11 import android.graphics.drawable.Drawable; 11 import android.graphics.drawable.Drawable;
12 import android.os.SystemClock; 12 import android.os.SystemClock;
13 import android.util.TypedValue; 13 import android.util.TypedValue;
14 import android.view.Gravity; 14 import android.view.Gravity;
15 import android.view.LayoutInflater; 15 import android.view.LayoutInflater;
16 import android.view.MotionEvent; 16 import android.view.MotionEvent;
17 import android.view.View; 17 import android.view.View;
18 import android.view.ViewConfiguration; 18 import android.view.ViewConfiguration;
19 import android.view.ViewGroup; 19 import android.view.ViewGroup;
20 import android.view.ViewParent; 20 import android.view.ViewParent;
21 import android.view.WindowManager; 21 import android.view.WindowManager;
22 import android.view.View.OnClickListener; 22 import android.view.View.OnClickListener;
23 import android.view.ViewGroup.LayoutParams; 23 import android.view.ViewGroup.LayoutParams;
24 import android.widget.PopupWindow; 24 import android.widget.PopupWindow;
25 import android.widget.TextView; 25 import android.widget.TextView;
26 26
27 import org.chromium.content.browser.PositionObserver;
28
27 /** 29 /**
28 * View that displays a selection or insertion handle for text editing. 30 * View that displays a selection or insertion handle for text editing.
31 *
32 * While a HandleView is logically a child of some other view, it does not exist in that View's
33 * hierarchy.
34 *
29 */ 35 */
30 public class HandleView extends View { 36 public class HandleView extends View {
31 private static final float FADE_DURATION = 200.f; 37 private static final float FADE_DURATION = 200.f;
32 38
33 private Drawable mDrawable; 39 private Drawable mDrawable;
34 private final PopupWindow mContainer; 40 private final PopupWindow mContainer;
41
42 // The position of the handle relative to the parent view.
35 private int mPositionX; 43 private int mPositionX;
36 private int mPositionY; 44 private int mPositionY;
45
46 // The position of the parent relative to the application's root view.
47 private int mParentPositionX;
48 private int mParentPositionY;
49
50 // The offset from this handles position to the "tip" of the handle.
51 private float mHotspotX;
52 private float mHotspotY;
53
37 private final CursorController mController; 54 private final CursorController mController;
38 private boolean mIsDragging; 55 private boolean mIsDragging;
39 private float mTouchToWindowOffsetX; 56 private float mTouchToWindowOffsetX;
40 private float mTouchToWindowOffsetY; 57 private float mTouchToWindowOffsetY;
41 private float mHotspotX; 58
42 private float mHotspotY;
43 private int mLineOffsetY; 59 private int mLineOffsetY;
44 private int mLastParentX;
45 private int mLastParentY;
46 private float mDownPositionX, mDownPositionY; 60 private float mDownPositionX, mDownPositionY;
47 private int mContainerPositionX, mContainerPositionY;
48 private long mTouchTimer; 61 private long mTouchTimer;
49 private boolean mIsInsertionHandle = false; 62 private boolean mIsInsertionHandle = false;
50 private float mAlpha; 63 private float mAlpha;
51 private long mFadeStartTime; 64 private long mFadeStartTime;
52 65
53 private View mParent; 66 private View mParent;
54 private InsertionHandleController.PastePopupMenu mPastePopupWindow; 67 private InsertionHandleController.PastePopupMenu mPastePopupWindow;
55 68
56 private final int mTextSelectHandleLeftRes; 69 private final int mTextSelectHandleLeftRes;
57 private final int mTextSelectHandleRightRes; 70 private final int mTextSelectHandleRightRes;
58 private final int mTextSelectHandleRes; 71 private final int mTextSelectHandleRes;
59 72
60 private Drawable mSelectHandleLeft; 73 private Drawable mSelectHandleLeft;
61 private Drawable mSelectHandleRight; 74 private Drawable mSelectHandleRight;
62 private Drawable mSelectHandleCenter; 75 private Drawable mSelectHandleCenter;
63 76
64 private final int[] mTempCoords = new int[2];
65 private final Rect mTempRect = new Rect(); 77 private final Rect mTempRect = new Rect();
66 78
67 static final int LEFT = 0; 79 static final int LEFT = 0;
68 static final int CENTER = 1; 80 static final int CENTER = 1;
69 static final int RIGHT = 2; 81 static final int RIGHT = 2;
70 82
83 private PositionObserver mParentPositionObserver;
84 private PositionObserver.Listener mParentPositionListener;
85
71 // Number of dips to subtract from the handle's y position to give a suitabl e 86 // Number of dips to subtract from the handle's y position to give a suitabl e
72 // y coordinate for the corresponding text position. This is to compensate f or the fact 87 // y coordinate for the corresponding text position. This is to compensate f or the fact
73 // that the handle position is at the base of the line of text. 88 // that the handle position is at the base of the line of text.
74 private static final float LINE_OFFSET_Y_DIP = 5.0f; 89 private static final float LINE_OFFSET_Y_DIP = 5.0f;
75 90
76 private static final int[] TEXT_VIEW_HANDLE_ATTRS = { 91 private static final int[] TEXT_VIEW_HANDLE_ATTRS = {
77 android.R.attr.textSelectHandleLeft, 92 android.R.attr.textSelectHandleLeft,
78 android.R.attr.textSelectHandle, 93 android.R.attr.textSelectHandle,
79 android.R.attr.textSelectHandleRight, 94 android.R.attr.textSelectHandleRight,
80 }; 95 };
81 96
82 HandleView(CursorController controller, int pos, View parent) { 97 HandleView(CursorController controller, int pos, View parent,
98 PositionObserver parentPositionObserver) {
83 super(parent.getContext()); 99 super(parent.getContext());
84 Context context = parent.getContext(); 100 Context context = parent.getContext();
85 mParent = parent; 101 mParent = parent;
86 mController = controller; 102 mController = controller;
87 mContainer = new PopupWindow(context, null, android.R.attr.textSelectHan dleWindowStyle); 103 mContainer = new PopupWindow(context, null, android.R.attr.textSelectHan dleWindowStyle);
88 mContainer.setSplitTouchEnabled(true); 104 mContainer.setSplitTouchEnabled(true);
89 mContainer.setClippingEnabled(false); 105 mContainer.setClippingEnabled(false);
90 106
91 TypedArray a = context.obtainStyledAttributes(TEXT_VIEW_HANDLE_ATTRS); 107 TypedArray a = context.obtainStyledAttributes(TEXT_VIEW_HANDLE_ATTRS);
92 mTextSelectHandleLeftRes = a.getResourceId(a.getIndex(LEFT), 0); 108 mTextSelectHandleLeftRes = a.getResourceId(a.getIndex(LEFT), 0);
93 mTextSelectHandleRes = a.getResourceId(a.getIndex(CENTER), 0); 109 mTextSelectHandleRes = a.getResourceId(a.getIndex(CENTER), 0);
94 mTextSelectHandleRightRes = a.getResourceId(a.getIndex(RIGHT), 0); 110 mTextSelectHandleRightRes = a.getResourceId(a.getIndex(RIGHT), 0);
95 a.recycle(); 111 a.recycle();
96 112
97 setOrientation(pos); 113 setOrientation(pos);
98 114
99 // Convert line offset dips to pixels. 115 // Convert line offset dips to pixels.
100 mLineOffsetY = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_D IP, 116 mLineOffsetY = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_D IP,
101 LINE_OFFSET_Y_DIP, context.getResources().getDisplayMetrics()); 117 LINE_OFFSET_Y_DIP, context.getResources().getDisplayMetrics());
102 118
103 mAlpha = 1.f; 119 mAlpha = 1.f;
120
121 mParentPositionListener = new PositionObserver.Listener() {
122 @Override
123 public void onPositionChanged(int x, int y) {
124 updateParentPosition(x, y);
125 }
126 };
127 mParentPositionObserver = parentPositionObserver;
104 } 128 }
105 129
106 void setOrientation(int pos) { 130 void setOrientation(int pos) {
107 int handleWidth; 131 int handleWidth;
108 switch (pos) { 132 switch (pos) {
109 case LEFT: { 133 case LEFT: {
110 if (mSelectHandleLeft == null) { 134 if (mSelectHandleLeft == null) {
111 mSelectHandleLeft = getContext().getResources().getDrawable( 135 mSelectHandleLeft = getContext().getResources().getDrawable(
112 mTextSelectHandleLeftRes); 136 mTextSelectHandleLeftRes);
113 } 137 }
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
145 mHotspotY = 0; 169 mHotspotY = 0;
146 invalidate(); 170 invalidate();
147 } 171 }
148 172
149 @Override 173 @Override
150 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 174 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
151 setMeasuredDimension(mDrawable.getIntrinsicWidth(), 175 setMeasuredDimension(mDrawable.getIntrinsicWidth(),
152 mDrawable.getIntrinsicHeight()); 176 mDrawable.getIntrinsicHeight());
153 } 177 }
154 178
155 private void updateContainerPosition() { 179 private void updateParentPosition(int parentPositionX, int parentPositionY) {
156 final int[] coords = mTempCoords; 180 // Hide paste popup window as soon as a scroll occurs.
157 mParent.getLocationInWindow(coords); 181 if (mPastePopupWindow != null) {
Ted C 2013/10/14 23:46:44 this all fits on one line
158 mContainerPositionX = coords[0] + mPositionX; 182 mPastePopupWindow.hide();
159 mContainerPositionY = coords[1] + mPositionY; 183 }
184
185 mTouchToWindowOffsetX += parentPositionX - mParentPositionX;
186 mTouchToWindowOffsetY += parentPositionY - mParentPositionY;
187 mParentPositionX = parentPositionX;
188 mParentPositionY = parentPositionY;
189 onPositionChanged();
190 }
191
192 private int getContainerPositionX() {
193 return mParentPositionX + mPositionX;
194 }
195
196 private int getContainerPositionY() {
197 return mParentPositionY + mPositionY;
198 }
199
200 private void onPositionChanged() {
201 mContainer.update(getContainerPositionX(), getContainerPositionY(),
202 getRight() - getLeft(), getBottom() - getTop());
203 }
204
205 private void showContainer() {
206 mContainer.showAtLocation(mParent, 0, getContainerPositionX(), getContai nerPositionY());
160 } 207 }
161 208
162 void show() { 209 void show() {
210 // While hidden, the parent position may have become stale. It must be u pdated before
211 // checking isPositionVisible().
212 updateParentPosition(mParentPositionObserver.getPositionX(),
213 mParentPositionObserver.getPositionY());
163 if (!isPositionVisible()) { 214 if (!isPositionVisible()) {
164 hide(); 215 hide();
165 return; 216 return;
166 } 217 }
218 mParentPositionObserver.addListener(mParentPositionListener);
167 mContainer.setContentView(this); 219 mContainer.setContentView(this);
168 updateContainerPosition(); 220 showContainer();
169 mContainer.showAtLocation(mParent, 0, mContainerPositionX, mContainerPos itionY);
170 221
171 // Hide paste view when handle is moved on screen. 222 // Hide paste view when handle is moved on screen.
172 if (mPastePopupWindow != null) { 223 if (mPastePopupWindow != null) {
173 mPastePopupWindow.hide(); 224 mPastePopupWindow.hide();
174 } 225 }
175 } 226 }
176 227
177 void hide() { 228 void hide() {
178 mIsDragging = false; 229 mIsDragging = false;
179 mContainer.dismiss(); 230 mContainer.dismiss();
231 mParentPositionObserver.removeListener(mParentPositionListener);
180 if (mPastePopupWindow != null) { 232 if (mPastePopupWindow != null) {
181 mPastePopupWindow.hide(); 233 mPastePopupWindow.hide();
182 } 234 }
183 } 235 }
184 236
185 boolean isShowing() { 237 boolean isShowing() {
186 return mContainer.isShowing(); 238 return mContainer.isShowing();
187 } 239 }
188 240
189 private boolean isPositionVisible() { 241 private boolean isPositionVisible() {
190 // Always show a dragging handle. 242 // Always show a dragging handle.
191 if (mIsDragging) { 243 if (mIsDragging) {
192 return true; 244 return true;
193 } 245 }
194 246
195 final Rect clip = mTempRect; 247 final Rect clip = mTempRect;
196 clip.left = 0; 248 clip.left = 0;
197 clip.top = 0; 249 clip.top = 0;
198 clip.right = mParent.getWidth(); 250 clip.right = mParent.getWidth();
199 clip.bottom = mParent.getHeight(); 251 clip.bottom = mParent.getHeight();
200 252
201 final ViewParent parent = mParent.getParent(); 253 final ViewParent parent = mParent.getParent();
202 if (parent == null || !parent.getChildVisibleRect(mParent, clip, null)) { 254 if (parent == null || !parent.getChildVisibleRect(mParent, clip, null)) {
203 return false; 255 return false;
204 } 256 }
205 257
206 final int[] coords = mTempCoords; 258 final int posX = getContainerPositionX() + (int) mHotspotX;
207 mParent.getLocationInWindow(coords); 259 final int posY = getContainerPositionY() + (int) mHotspotY;
208 final int posX = coords[0] + mPositionX + (int) mHotspotX;
209 final int posY = coords[1] + mPositionY + (int) mHotspotY;
210 260
211 return posX >= clip.left && posX <= clip.right && 261 return posX >= clip.left && posX <= clip.right &&
212 posY >= clip.top && posY <= clip.bottom; 262 posY >= clip.top && posY <= clip.bottom;
213 } 263 }
214 264
215 // x and y are in physical pixels. 265 // x and y are in physical pixels.
216 void moveTo(int x, int y) { 266 void moveTo(int x, int y) {
267 int previousPositionX = mPositionX;
268 int previousPositionY = mPositionY;
269
217 mPositionX = x; 270 mPositionX = x;
218 mPositionY = y; 271 mPositionY = y;
219 if (isPositionVisible()) { 272 if (isPositionVisible()) {
220 int[] coords = null;
221 if (mContainer.isShowing()) { 273 if (mContainer.isShowing()) {
222 coords = mTempCoords; 274 onPositionChanged();
223 mParent.getLocationInWindow(coords); 275 // Hide paste popup window as soon as the handle is dragged.
224 final int containerPositionX = coords[0] + mPositionX; 276 if (mPastePopupWindow != null &&
225 final int containerPositionY = coords[1] + mPositionY; 277 (previousPositionX != mPositionX || previousPositionY != mPositionY)) {
226 278 mPastePopupWindow.hide();
227 if (containerPositionX != mContainerPositionX ||
228 containerPositionY != mContainerPositionY) {
229 mContainerPositionX = containerPositionX;
230 mContainerPositionY = containerPositionY;
231
232 mContainer.update(mContainerPositionX, mContainerPositionY,
233 getRight() - getLeft(), getBottom() - getTop());
234
235 // Hide paste popup window as soon as a scroll occurs.
236 if (mPastePopupWindow != null) {
237 mPastePopupWindow.hide();
238 }
239 } 279 }
240 } else { 280 } else {
241 show(); 281 show();
242 } 282 }
243 283
244 if (mIsDragging) { 284 if (mIsDragging) {
245 if (coords == null) {
246 coords = mTempCoords;
247 mParent.getLocationInWindow(coords);
248 }
249 if (coords[0] != mLastParentX || coords[1] != mLastParentY) {
250 mTouchToWindowOffsetX += coords[0] - mLastParentX;
251 mTouchToWindowOffsetY += coords[1] - mLastParentY;
252 mLastParentX = coords[0];
253 mLastParentY = coords[1];
254 }
255 // Hide paste popup window as soon as the handle is dragged. 285 // Hide paste popup window as soon as the handle is dragged.
256 if (mPastePopupWindow != null) { 286 if (mPastePopupWindow != null) {
257 mPastePopupWindow.hide(); 287 mPastePopupWindow.hide();
258 } 288 }
259 } 289 }
260 } else { 290 } else {
261 hide(); 291 hide();
262 } 292 }
263 } 293 }
264 294
265 @Override 295 @Override
266 protected void onDraw(Canvas c) { 296 protected void onDraw(Canvas c) {
267 updateAlpha(); 297 updateAlpha();
268 updateContainerPosition();
269 mContainer.update(mContainerPositionX, mContainerPositionY,
270 getRight() - getLeft(), getBottom() - getTop());
271 mDrawable.setBounds(0, 0, getRight() - getLeft(), getBottom() - getTop() ); 298 mDrawable.setBounds(0, 0, getRight() - getLeft(), getBottom() - getTop() );
272 mDrawable.draw(c); 299 mDrawable.draw(c);
273 } 300 }
274 301
275 @Override 302 @Override
276 public boolean onTouchEvent(MotionEvent ev) { 303 public boolean onTouchEvent(MotionEvent ev) {
277 switch (ev.getActionMasked()) { 304 switch (ev.getActionMasked()) {
278 case MotionEvent.ACTION_DOWN: { 305 case MotionEvent.ACTION_DOWN: {
279 mDownPositionX = ev.getRawX(); 306 mDownPositionX = ev.getRawX();
280 mDownPositionY = ev.getRawY(); 307 mDownPositionY = ev.getRawY();
281 mTouchToWindowOffsetX = mDownPositionX - mPositionX; 308 mTouchToWindowOffsetX = mDownPositionX - mPositionX;
282 mTouchToWindowOffsetY = mDownPositionY - mPositionY; 309 mTouchToWindowOffsetY = mDownPositionY - mPositionY;
283 final int[] coords = mTempCoords;
284 mParent.getLocationInWindow(coords);
285 mLastParentX = coords[0];
286 mLastParentY = coords[1];
287 mIsDragging = true; 310 mIsDragging = true;
288 mController.beforeStartUpdatingPosition(this); 311 mController.beforeStartUpdatingPosition(this);
289 mTouchTimer = SystemClock.uptimeMillis(); 312 mTouchTimer = SystemClock.uptimeMillis();
290 break; 313 break;
291 } 314 }
292 315
293 case MotionEvent.ACTION_MOVE: { 316 case MotionEvent.ACTION_MOVE: {
294 updatePosition(ev.getRawX(), ev.getRawY()); 317 updatePosition(ev.getRawX(), ev.getRawY());
295 break; 318 break;
296 } 319 }
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
340 363
341 private void updatePosition(float rawX, float rawY) { 364 private void updatePosition(float rawX, float rawY) {
342 final float newPosX = rawX - mTouchToWindowOffsetX + mHotspotX; 365 final float newPosX = rawX - mTouchToWindowOffsetX + mHotspotX;
343 final float newPosY = rawY - mTouchToWindowOffsetY + mHotspotY - mLineOf fsetY; 366 final float newPosY = rawY - mTouchToWindowOffsetY + mHotspotY - mLineOf fsetY;
344 367
345 mController.updatePosition(this, Math.round(newPosX), Math.round(newPosY )); 368 mController.updatePosition(this, Math.round(newPosX), Math.round(newPosY ));
346 } 369 }
347 370
348 // x and y are in physical pixels. 371 // x and y are in physical pixels.
349 void positionAt(int x, int y) { 372 void positionAt(int x, int y) {
350 moveTo((int)(x - mHotspotX), (int)(y - mHotspotY)); 373 moveTo(x - Math.round(mHotspotX), y - Math.round(mHotspotY));
351 } 374 }
352 375
353 // Returns the x coordinate of the position that the handle appears to be po inting to. 376 // Returns the x coordinate of the position that the handle appears to be po inting to relative
377 // to the handles "parent" view.
354 int getAdjustedPositionX() { 378 int getAdjustedPositionX() {
355 return (int) (mPositionX + mHotspotX); 379 return mPositionX + Math.round(mHotspotX);
356 } 380 }
357 381
358 // Returns the y coordinate of the position that the handle appears to be po inting to. 382 // Returns the y coordinate of the position that the handle appears to be po inting to relative
383 // to the handles "parent" view.
359 int getAdjustedPositionY() { 384 int getAdjustedPositionY() {
360 return (int) (mPositionY + mHotspotY); 385 return mPositionY + Math.round(mHotspotY);
386 }
387
388 // Returns the x coordinate of the postion that the handle appears to be poi nting to relative to
389 // the root view of the application.
390 int getRootViewRelativePositionX() {
391 return getContainerPositionX() + Math.round(mHotspotX);
392 }
393
394 // Returns the y coordinate of the postion that the handle appears to be poi nting to relative to
395 // the root view of the application.
396 int getRootViewRelativePositionY() {
397 return getContainerPositionY() + Math.round(mHotspotY);
361 } 398 }
362 399
363 // Returns a suitable y coordinate for the text position corresponding to th e handle. 400 // Returns a suitable y coordinate for the text position corresponding to th e handle.
364 // As the handle points to a position on the base of the line of text, this method 401 // As the handle points to a position on the base of the line of text, this method
365 // returns a coordinate a small number of pixels higher (i.e. a slightly sma ller number) 402 // returns a coordinate a small number of pixels higher (i.e. a slightly sma ller number)
366 // than getAdjustedPositionY. 403 // than getAdjustedPositionY.
367 int getLineAdjustedPositionY() { 404 int getLineAdjustedPositionY() {
368 return (int) (mPositionY + mHotspotY - mLineOffsetY); 405 return (int) (mPositionY + mHotspotY - mLineOffsetY);
369 } 406 }
370 407
(...skipping 22 matching lines...) Expand all
393 InsertionHandleController ihc = (InsertionHandleController) mController; 430 InsertionHandleController ihc = (InsertionHandleController) mController;
394 if (mIsInsertionHandle && ihc.canPaste()) { 431 if (mIsInsertionHandle && ihc.canPaste()) {
395 if (mPastePopupWindow == null) { 432 if (mPastePopupWindow == null) {
396 // Lazy initialization: create when actually shown only. 433 // Lazy initialization: create when actually shown only.
397 mPastePopupWindow = ihc.new PastePopupMenu(); 434 mPastePopupWindow = ihc.new PastePopupMenu();
398 } 435 }
399 mPastePopupWindow.show(); 436 mPastePopupWindow.show();
400 } 437 }
401 } 438 }
402 } 439 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698