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

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: 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.base.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.
29 */ 31 */
30 public class HandleView extends View { 32 public class HandleView extends View {
31 private static final float FADE_DURATION = 200.f; 33 private static final float FADE_DURATION = 200.f;
32 34
33 private Drawable mDrawable; 35 private Drawable mDrawable;
34 private final PopupWindow mContainer; 36 private final PopupWindow mContainer;
35 private int mPositionX; 37 private int mPositionX;
36 private int mPositionY; 38 private int mPositionY;
39 private int mParentPositionX;
40 private int mParentPositionY;
37 private final CursorController mController; 41 private final CursorController mController;
38 private boolean mIsDragging; 42 private boolean mIsDragging;
39 private float mTouchToWindowOffsetX; 43 private float mTouchToWindowOffsetX;
40 private float mTouchToWindowOffsetY; 44 private float mTouchToWindowOffsetY;
41 private float mHotspotX; 45 private float mHotspotX;
42 private float mHotspotY; 46 private float mHotspotY;
43 private int mLineOffsetY; 47 private int mLineOffsetY;
44 private int mLastParentX;
45 private int mLastParentY;
46 private float mDownPositionX, mDownPositionY; 48 private float mDownPositionX, mDownPositionY;
47 private int mContainerPositionX, mContainerPositionY;
48 private long mTouchTimer; 49 private long mTouchTimer;
49 private boolean mIsInsertionHandle = false; 50 private boolean mIsInsertionHandle = false;
50 private float mAlpha; 51 private float mAlpha;
51 private long mFadeStartTime; 52 private long mFadeStartTime;
52 53
53 private View mParent; 54 private View mParent;
54 private InsertionHandleController.PastePopupMenu mPastePopupWindow; 55 private InsertionHandleController.PastePopupMenu mPastePopupWindow;
55 56
56 private final int mTextSelectHandleLeftRes; 57 private final int mTextSelectHandleLeftRes;
57 private final int mTextSelectHandleRightRes; 58 private final int mTextSelectHandleRightRes;
58 private final int mTextSelectHandleRes; 59 private final int mTextSelectHandleRes;
59 60
60 private Drawable mSelectHandleLeft; 61 private Drawable mSelectHandleLeft;
61 private Drawable mSelectHandleRight; 62 private Drawable mSelectHandleRight;
62 private Drawable mSelectHandleCenter; 63 private Drawable mSelectHandleCenter;
63 64
64 private final int[] mTempCoords = new int[2];
65 private final Rect mTempRect = new Rect(); 65 private final Rect mTempRect = new Rect();
66 66
67 static final int LEFT = 0; 67 static final int LEFT = 0;
68 static final int CENTER = 1; 68 static final int CENTER = 1;
69 static final int RIGHT = 2; 69 static final int RIGHT = 2;
70 70
71 private PositionObserver mParentPositionObserver;
72 private PositionObserver.Listener mParentPositionListener;
73
71 // Number of dips to subtract from the handle's y position to give a suitabl e 74 // 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 75 // 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. 76 // that the handle position is at the base of the line of text.
74 private static final float LINE_OFFSET_Y_DIP = 5.0f; 77 private static final float LINE_OFFSET_Y_DIP = 5.0f;
75 78
76 private static final int[] TEXT_VIEW_HANDLE_ATTRS = { 79 private static final int[] TEXT_VIEW_HANDLE_ATTRS = {
77 android.R.attr.textSelectHandleLeft, 80 android.R.attr.textSelectHandleLeft,
78 android.R.attr.textSelectHandle, 81 android.R.attr.textSelectHandle,
79 android.R.attr.textSelectHandleRight, 82 android.R.attr.textSelectHandleRight,
80 }; 83 };
81 84
82 HandleView(CursorController controller, int pos, View parent) { 85 HandleView(CursorController controller, int pos, View parent,
86 PositionObserver parentPositionObserver) {
83 super(parent.getContext()); 87 super(parent.getContext());
84 Context context = parent.getContext(); 88 Context context = parent.getContext();
85 mParent = parent; 89 mParent = parent;
86 mController = controller; 90 mController = controller;
91
87 mContainer = new PopupWindow(context, null, android.R.attr.textSelectHan dleWindowStyle); 92 mContainer = new PopupWindow(context, null, android.R.attr.textSelectHan dleWindowStyle);
88 mContainer.setSplitTouchEnabled(true); 93 mContainer.setSplitTouchEnabled(true);
89 mContainer.setClippingEnabled(false); 94 mContainer.setClippingEnabled(false);
90 95
91 TypedArray a = context.obtainStyledAttributes(TEXT_VIEW_HANDLE_ATTRS); 96 TypedArray a = context.obtainStyledAttributes(TEXT_VIEW_HANDLE_ATTRS);
92 mTextSelectHandleLeftRes = a.getResourceId(a.getIndex(LEFT), 0); 97 mTextSelectHandleLeftRes = a.getResourceId(a.getIndex(LEFT), 0);
93 mTextSelectHandleRes = a.getResourceId(a.getIndex(CENTER), 0); 98 mTextSelectHandleRes = a.getResourceId(a.getIndex(CENTER), 0);
94 mTextSelectHandleRightRes = a.getResourceId(a.getIndex(RIGHT), 0); 99 mTextSelectHandleRightRes = a.getResourceId(a.getIndex(RIGHT), 0);
95 a.recycle(); 100 a.recycle();
96 101
97 setOrientation(pos); 102 setOrientation(pos);
98 103
99 // Convert line offset dips to pixels. 104 // Convert line offset dips to pixels.
100 mLineOffsetY = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_D IP, 105 mLineOffsetY = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_D IP,
101 LINE_OFFSET_Y_DIP, context.getResources().getDisplayMetrics()); 106 LINE_OFFSET_Y_DIP, context.getResources().getDisplayMetrics());
102 107
103 mAlpha = 1.f; 108 mAlpha = 1.f;
109
110 mParentPositionListener = new PositionObserver.Listener() {
111 @Override
112 public void onPositionChanged(int x, int y) {
113 updateParentPosition(x, y);
114 }
115 };
116 mParentPositionObserver = parentPositionObserver;
104 } 117 }
105 118
106 void setOrientation(int pos) { 119 void setOrientation(int pos) {
107 int handleWidth; 120 int handleWidth;
108 switch (pos) { 121 switch (pos) {
109 case LEFT: { 122 case LEFT: {
110 if (mSelectHandleLeft == null) { 123 if (mSelectHandleLeft == null) {
111 mSelectHandleLeft = getContext().getResources().getDrawable( 124 mSelectHandleLeft = getContext().getResources().getDrawable(
112 mTextSelectHandleLeftRes); 125 mTextSelectHandleLeftRes);
113 } 126 }
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
145 mHotspotY = 0; 158 mHotspotY = 0;
146 invalidate(); 159 invalidate();
147 } 160 }
148 161
149 @Override 162 @Override
150 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 163 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
151 setMeasuredDimension(mDrawable.getIntrinsicWidth(), 164 setMeasuredDimension(mDrawable.getIntrinsicWidth(),
152 mDrawable.getIntrinsicHeight()); 165 mDrawable.getIntrinsicHeight());
153 } 166 }
154 167
155 private void updateContainerPosition() { 168 private void updateParentPosition(int parentPositionX, int parentPositionY) {
156 final int[] coords = mTempCoords; 169 // Hide paste popup window as soon as a scroll occurs.
157 mParent.getLocationInWindow(coords); 170 if (mPastePopupWindow != null) {
158 mContainerPositionX = coords[0] + mPositionX; 171 mPastePopupWindow.hide();
159 mContainerPositionY = coords[1] + mPositionY; 172 }
173
174 mTouchToWindowOffsetX += parentPositionX - mParentPositionX;
175 mTouchToWindowOffsetY += parentPositionY - mParentPositionY;
176 mParentPositionX = parentPositionX;
177 mParentPositionY = parentPositionY;
178 onPositionChanged();
179 }
180
181 private int getContainerPositionX() {
182 return mParentPositionX + mPositionX;
183 }
184
185 private int getContainerPositionY() {
186 return mParentPositionY + mPositionY;
187 }
188
189 private void onPositionChanged() {
190 mContainer.update(getContainerPositionX(), getContainerPositionY(),
191 getRight() - getLeft(), getBottom() - getTop());
192 }
193
194 private void showContainer() {
195 mContainer.showAtLocation(mParent, 0, getContainerPositionX(), getContai nerPositionY());
160 } 196 }
161 197
162 void show() { 198 void show() {
199 // While hidden, the parent position may have become stale. It must be u pdated before
200 // checking isPositionVisible().
201 updateParentPosition(mParentPositionObserver.getPositionX(),
202 mParentPositionObserver.getPositionY());
163 if (!isPositionVisible()) { 203 if (!isPositionVisible()) {
164 hide(); 204 hide();
165 return; 205 return;
166 } 206 }
207 mParentPositionObserver.addListener(mParentPositionListener);
167 mContainer.setContentView(this); 208 mContainer.setContentView(this);
168 updateContainerPosition(); 209 showContainer();
169 mContainer.showAtLocation(mParent, 0, mContainerPositionX, mContainerPos itionY);
170 210
171 // Hide paste view when handle is moved on screen. 211 // Hide paste view when handle is moved on screen.
172 if (mPastePopupWindow != null) { 212 if (mPastePopupWindow != null) {
173 mPastePopupWindow.hide(); 213 mPastePopupWindow.hide();
174 } 214 }
175 } 215 }
176 216
177 void hide() { 217 void hide() {
178 mIsDragging = false; 218 mIsDragging = false;
179 mContainer.dismiss(); 219 mContainer.dismiss();
220 mParentPositionObserver.removeListener(mParentPositionListener);
180 if (mPastePopupWindow != null) { 221 if (mPastePopupWindow != null) {
181 mPastePopupWindow.hide(); 222 mPastePopupWindow.hide();
182 } 223 }
183 } 224 }
184 225
185 boolean isShowing() { 226 boolean isShowing() {
186 return mContainer.isShowing(); 227 return mContainer.isShowing();
187 } 228 }
188 229
189 private boolean isPositionVisible() { 230 private boolean isPositionVisible() {
190 // Always show a dragging handle. 231 // Always show a dragging handle.
191 if (mIsDragging) { 232 if (mIsDragging) {
192 return true; 233 return true;
193 } 234 }
194 235
195 final Rect clip = mTempRect; 236 final Rect clip = mTempRect;
196 clip.left = 0; 237 clip.left = 0;
197 clip.top = 0; 238 clip.top = 0;
198 clip.right = mParent.getWidth(); 239 clip.right = mParent.getWidth();
199 clip.bottom = mParent.getHeight(); 240 clip.bottom = mParent.getHeight();
200 241
201 final ViewParent parent = mParent.getParent(); 242 final ViewParent parent = mParent.getParent();
202 if (parent == null || !parent.getChildVisibleRect(mParent, clip, null)) { 243 if (parent == null || !parent.getChildVisibleRect(mParent, clip, null)) {
203 return false; 244 return false;
204 } 245 }
205 246
206 final int[] coords = mTempCoords; 247 final int posX = getContainerPositionX() + (int) mHotspotX;
207 mParent.getLocationInWindow(coords); 248 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 249
211 return posX >= clip.left && posX <= clip.right && 250 return posX >= clip.left && posX <= clip.right &&
212 posY >= clip.top && posY <= clip.bottom; 251 posY >= clip.top && posY <= clip.bottom;
213 } 252 }
214 253
215 // x and y are in physical pixels. 254 // x and y are in physical pixels.
216 void moveTo(int x, int y) { 255 void moveTo(int x, int y) {
256 int previousPositionX = mPositionX;
257 int previousPositionY = mPositionY;
258
217 mPositionX = x; 259 mPositionX = x;
218 mPositionY = y; 260 mPositionY = y;
219 if (isPositionVisible()) { 261 if (isPositionVisible()) {
220 int[] coords = null;
221 if (mContainer.isShowing()) { 262 if (mContainer.isShowing()) {
222 coords = mTempCoords; 263 onPositionChanged();
223 mParent.getLocationInWindow(coords); 264 // Hide paste popup window as soon as the handle is dragged.
224 final int containerPositionX = coords[0] + mPositionX; 265 if (mPastePopupWindow != null &&
225 final int containerPositionY = coords[1] + mPositionY; 266 (previousPositionX != mPositionX || previousPositionY != mPositionY)) {
226 267 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 } 268 }
240 } else { 269 } else {
241 show(); 270 show();
242 } 271 }
243 272
244 if (mIsDragging) { 273 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. 274 // Hide paste popup window as soon as the handle is dragged.
256 if (mPastePopupWindow != null) { 275 if (mPastePopupWindow != null) {
257 mPastePopupWindow.hide(); 276 mPastePopupWindow.hide();
258 } 277 }
259 } 278 }
260 } else { 279 } else {
261 hide(); 280 hide();
262 } 281 }
263 } 282 }
264 283
265 @Override 284 @Override
266 protected void onDraw(Canvas c) { 285 protected void onDraw(Canvas c) {
267 updateAlpha(); 286 updateAlpha();
268 updateContainerPosition();
269 mContainer.update(mContainerPositionX, mContainerPositionY,
270 getRight() - getLeft(), getBottom() - getTop());
271 mDrawable.setBounds(0, 0, getRight() - getLeft(), getBottom() - getTop() ); 287 mDrawable.setBounds(0, 0, getRight() - getLeft(), getBottom() - getTop() );
272 mDrawable.draw(c); 288 mDrawable.draw(c);
273 } 289 }
274 290
275 @Override 291 @Override
276 public boolean onTouchEvent(MotionEvent ev) { 292 public boolean onTouchEvent(MotionEvent ev) {
277 switch (ev.getActionMasked()) { 293 switch (ev.getActionMasked()) {
278 case MotionEvent.ACTION_DOWN: { 294 case MotionEvent.ACTION_DOWN: {
279 mDownPositionX = ev.getRawX(); 295 mDownPositionX = ev.getRawX();
280 mDownPositionY = ev.getRawY(); 296 mDownPositionY = ev.getRawY();
281 mTouchToWindowOffsetX = mDownPositionX - mPositionX; 297 mTouchToWindowOffsetX = mDownPositionX - mPositionX;
282 mTouchToWindowOffsetY = mDownPositionY - mPositionY; 298 mTouchToWindowOffsetY = mDownPositionY - mPositionY;
283 final int[] coords = mTempCoords;
284 mParent.getLocationInWindow(coords);
285 mLastParentX = coords[0];
286 mLastParentY = coords[1];
287 mIsDragging = true; 299 mIsDragging = true;
288 mController.beforeStartUpdatingPosition(this); 300 mController.beforeStartUpdatingPosition(this);
289 mTouchTimer = SystemClock.uptimeMillis(); 301 mTouchTimer = SystemClock.uptimeMillis();
290 break; 302 break;
291 } 303 }
292 304
293 case MotionEvent.ACTION_MOVE: { 305 case MotionEvent.ACTION_MOVE: {
294 updatePosition(ev.getRawX(), ev.getRawY()); 306 updatePosition(ev.getRawX(), ev.getRawY());
295 break; 307 break;
296 } 308 }
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
393 InsertionHandleController ihc = (InsertionHandleController) mController; 405 InsertionHandleController ihc = (InsertionHandleController) mController;
394 if (mIsInsertionHandle && ihc.canPaste()) { 406 if (mIsInsertionHandle && ihc.canPaste()) {
395 if (mPastePopupWindow == null) { 407 if (mPastePopupWindow == null) {
396 // Lazy initialization: create when actually shown only. 408 // Lazy initialization: create when actually shown only.
397 mPastePopupWindow = ihc.new PastePopupMenu(); 409 mPastePopupWindow = ihc.new PastePopupMenu();
398 } 410 }
399 mPastePopupWindow.show(); 411 mPastePopupWindow.show();
400 } 412 }
401 } 413 }
402 } 414 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698