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

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 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) mPastePopupWindow.hide();
158 mContainerPositionX = coords[0] + mPositionX; 182
159 mContainerPositionY = coords[1] + mPositionY; 183 mTouchToWindowOffsetX += parentPositionX - mParentPositionX;
184 mTouchToWindowOffsetY += parentPositionY - mParentPositionY;
185 mParentPositionX = parentPositionX;
186 mParentPositionY = parentPositionY;
187 onPositionChanged();
188 }
189
190 private int getContainerPositionX() {
191 return mParentPositionX + mPositionX;
192 }
193
194 private int getContainerPositionY() {
195 return mParentPositionY + mPositionY;
196 }
197
198 private void onPositionChanged() {
199 mContainer.update(getContainerPositionX(), getContainerPositionY(),
200 getRight() - getLeft(), getBottom() - getTop());
201 }
202
203 private void showContainer() {
204 mContainer.showAtLocation(mParent, 0, getContainerPositionX(), getContai nerPositionY());
160 } 205 }
161 206
162 void show() { 207 void show() {
208 // While hidden, the parent position may have become stale. It must be u pdated before
209 // checking isPositionVisible().
210 updateParentPosition(mParentPositionObserver.getPositionX(),
211 mParentPositionObserver.getPositionY());
163 if (!isPositionVisible()) { 212 if (!isPositionVisible()) {
164 hide(); 213 hide();
165 return; 214 return;
166 } 215 }
216 mParentPositionObserver.addListener(mParentPositionListener);
167 mContainer.setContentView(this); 217 mContainer.setContentView(this);
168 updateContainerPosition(); 218 showContainer();
169 mContainer.showAtLocation(mParent, 0, mContainerPositionX, mContainerPos itionY);
170 219
171 // Hide paste view when handle is moved on screen. 220 // Hide paste view when handle is moved on screen.
172 if (mPastePopupWindow != null) { 221 if (mPastePopupWindow != null) {
173 mPastePopupWindow.hide(); 222 mPastePopupWindow.hide();
174 } 223 }
175 } 224 }
176 225
177 void hide() { 226 void hide() {
178 mIsDragging = false; 227 mIsDragging = false;
179 mContainer.dismiss(); 228 mContainer.dismiss();
229 mParentPositionObserver.removeListener(mParentPositionListener);
180 if (mPastePopupWindow != null) { 230 if (mPastePopupWindow != null) {
181 mPastePopupWindow.hide(); 231 mPastePopupWindow.hide();
182 } 232 }
183 } 233 }
184 234
185 boolean isShowing() { 235 boolean isShowing() {
186 return mContainer.isShowing(); 236 return mContainer.isShowing();
187 } 237 }
188 238
189 private boolean isPositionVisible() { 239 private boolean isPositionVisible() {
190 // Always show a dragging handle. 240 // Always show a dragging handle.
191 if (mIsDragging) { 241 if (mIsDragging) {
192 return true; 242 return true;
193 } 243 }
194 244
195 final Rect clip = mTempRect; 245 final Rect clip = mTempRect;
196 clip.left = 0; 246 clip.left = 0;
197 clip.top = 0; 247 clip.top = 0;
198 clip.right = mParent.getWidth(); 248 clip.right = mParent.getWidth();
199 clip.bottom = mParent.getHeight(); 249 clip.bottom = mParent.getHeight();
200 250
201 final ViewParent parent = mParent.getParent(); 251 final ViewParent parent = mParent.getParent();
202 if (parent == null || !parent.getChildVisibleRect(mParent, clip, null)) { 252 if (parent == null || !parent.getChildVisibleRect(mParent, clip, null)) {
203 return false; 253 return false;
204 } 254 }
205 255
206 final int[] coords = mTempCoords; 256 final int posX = getContainerPositionX() + (int) mHotspotX;
207 mParent.getLocationInWindow(coords); 257 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 258
211 return posX >= clip.left && posX <= clip.right && 259 return posX >= clip.left && posX <= clip.right &&
212 posY >= clip.top && posY <= clip.bottom; 260 posY >= clip.top && posY <= clip.bottom;
213 } 261 }
214 262
215 // x and y are in physical pixels. 263 // x and y are in physical pixels.
216 void moveTo(int x, int y) { 264 void moveTo(int x, int y) {
265 int previousPositionX = mPositionX;
266 int previousPositionY = mPositionY;
267
217 mPositionX = x; 268 mPositionX = x;
218 mPositionY = y; 269 mPositionY = y;
219 if (isPositionVisible()) { 270 if (isPositionVisible()) {
220 int[] coords = null;
221 if (mContainer.isShowing()) { 271 if (mContainer.isShowing()) {
222 coords = mTempCoords; 272 onPositionChanged();
223 mParent.getLocationInWindow(coords); 273 // Hide paste popup window as soon as the handle is dragged.
224 final int containerPositionX = coords[0] + mPositionX; 274 if (mPastePopupWindow != null &&
225 final int containerPositionY = coords[1] + mPositionY; 275 (previousPositionX != mPositionX || previousPositionY != mPositionY)) {
226 276 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 } 277 }
240 } else { 278 } else {
241 show(); 279 show();
242 } 280 }
243 281
244 if (mIsDragging) { 282 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. 283 // Hide paste popup window as soon as the handle is dragged.
256 if (mPastePopupWindow != null) { 284 if (mPastePopupWindow != null) {
257 mPastePopupWindow.hide(); 285 mPastePopupWindow.hide();
258 } 286 }
259 } 287 }
260 } else { 288 } else {
261 hide(); 289 hide();
262 } 290 }
263 } 291 }
264 292
265 @Override 293 @Override
266 protected void onDraw(Canvas c) { 294 protected void onDraw(Canvas c) {
267 updateAlpha(); 295 updateAlpha();
268 updateContainerPosition();
269 mContainer.update(mContainerPositionX, mContainerPositionY,
270 getRight() - getLeft(), getBottom() - getTop());
271 mDrawable.setBounds(0, 0, getRight() - getLeft(), getBottom() - getTop() ); 296 mDrawable.setBounds(0, 0, getRight() - getLeft(), getBottom() - getTop() );
272 mDrawable.draw(c); 297 mDrawable.draw(c);
273 } 298 }
274 299
275 @Override 300 @Override
276 public boolean onTouchEvent(MotionEvent ev) { 301 public boolean onTouchEvent(MotionEvent ev) {
277 switch (ev.getActionMasked()) { 302 switch (ev.getActionMasked()) {
278 case MotionEvent.ACTION_DOWN: { 303 case MotionEvent.ACTION_DOWN: {
279 mDownPositionX = ev.getRawX(); 304 mDownPositionX = ev.getRawX();
280 mDownPositionY = ev.getRawY(); 305 mDownPositionY = ev.getRawY();
281 mTouchToWindowOffsetX = mDownPositionX - mPositionX; 306 mTouchToWindowOffsetX = mDownPositionX - mPositionX;
282 mTouchToWindowOffsetY = mDownPositionY - mPositionY; 307 mTouchToWindowOffsetY = mDownPositionY - mPositionY;
283 final int[] coords = mTempCoords;
284 mParent.getLocationInWindow(coords);
285 mLastParentX = coords[0];
286 mLastParentY = coords[1];
287 mIsDragging = true; 308 mIsDragging = true;
288 mController.beforeStartUpdatingPosition(this); 309 mController.beforeStartUpdatingPosition(this);
289 mTouchTimer = SystemClock.uptimeMillis(); 310 mTouchTimer = SystemClock.uptimeMillis();
290 break; 311 break;
291 } 312 }
292 313
293 case MotionEvent.ACTION_MOVE: { 314 case MotionEvent.ACTION_MOVE: {
294 updatePosition(ev.getRawX(), ev.getRawY()); 315 updatePosition(ev.getRawX(), ev.getRawY());
295 break; 316 break;
296 } 317 }
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
340 361
341 private void updatePosition(float rawX, float rawY) { 362 private void updatePosition(float rawX, float rawY) {
342 final float newPosX = rawX - mTouchToWindowOffsetX + mHotspotX; 363 final float newPosX = rawX - mTouchToWindowOffsetX + mHotspotX;
343 final float newPosY = rawY - mTouchToWindowOffsetY + mHotspotY - mLineOf fsetY; 364 final float newPosY = rawY - mTouchToWindowOffsetY + mHotspotY - mLineOf fsetY;
344 365
345 mController.updatePosition(this, Math.round(newPosX), Math.round(newPosY )); 366 mController.updatePosition(this, Math.round(newPosX), Math.round(newPosY ));
346 } 367 }
347 368
348 // x and y are in physical pixels. 369 // x and y are in physical pixels.
349 void positionAt(int x, int y) { 370 void positionAt(int x, int y) {
350 moveTo((int)(x - mHotspotX), (int)(y - mHotspotY)); 371 moveTo(x - Math.round(mHotspotX), y - Math.round(mHotspotY));
351 } 372 }
352 373
353 // Returns the x coordinate of the position that the handle appears to be po inting to. 374 // Returns the x coordinate of the position that the handle appears to be po inting to relative
375 // to the handles "parent" view.
354 int getAdjustedPositionX() { 376 int getAdjustedPositionX() {
355 return (int) (mPositionX + mHotspotX); 377 return mPositionX + Math.round(mHotspotX);
356 } 378 }
357 379
358 // Returns the y coordinate of the position that the handle appears to be po inting to. 380 // Returns the y coordinate of the position that the handle appears to be po inting to relative
381 // to the handles "parent" view.
359 int getAdjustedPositionY() { 382 int getAdjustedPositionY() {
360 return (int) (mPositionY + mHotspotY); 383 return mPositionY + Math.round(mHotspotY);
384 }
385
386 // Returns the x coordinate of the postion that the handle appears to be poi nting to relative to
387 // the root view of the application.
388 int getRootViewRelativePositionX() {
389 return getContainerPositionX() + Math.round(mHotspotX);
390 }
391
392 // Returns the y coordinate of the postion that the handle appears to be poi nting to relative to
393 // the root view of the application.
394 int getRootViewRelativePositionY() {
395 return getContainerPositionY() + Math.round(mHotspotY);
361 } 396 }
362 397
363 // Returns a suitable y coordinate for the text position corresponding to th e handle. 398 // 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 399 // 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) 400 // returns a coordinate a small number of pixels higher (i.e. a slightly sma ller number)
366 // than getAdjustedPositionY. 401 // than getAdjustedPositionY.
367 int getLineAdjustedPositionY() { 402 int getLineAdjustedPositionY() {
368 return (int) (mPositionY + mHotspotY - mLineOffsetY); 403 return (int) (mPositionY + mHotspotY - mLineOffsetY);
369 } 404 }
370 405
(...skipping 22 matching lines...) Expand all
393 InsertionHandleController ihc = (InsertionHandleController) mController; 428 InsertionHandleController ihc = (InsertionHandleController) mController;
394 if (mIsInsertionHandle && ihc.canPaste()) { 429 if (mIsInsertionHandle && ihc.canPaste()) {
395 if (mPastePopupWindow == null) { 430 if (mPastePopupWindow == null) {
396 // Lazy initialization: create when actually shown only. 431 // Lazy initialization: create when actually shown only.
397 mPastePopupWindow = ihc.new PastePopupMenu(); 432 mPastePopupWindow = ihc.new PastePopupMenu();
398 } 433 }
399 mPastePopupWindow.show(); 434 mPastePopupWindow.show();
400 } 435 }
401 } 436 }
402 } 437 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698