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

Side by Side Diff: ui/touch_selection/touch_handle.cc

Issue 481683003: Support for Adaptive Handle Orientation (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebased Created 5 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
« no previous file with comments | « ui/touch_selection/touch_handle.h ('k') | ui/touch_selection/touch_handle_drawable_aura.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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 #include "ui/touch_selection/touch_handle.h" 5 #include "ui/touch_selection/touch_handle.h"
6 6
7 #include <cmath> 7 #include <cmath>
8 8
9 namespace ui { 9 namespace ui {
10 10
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
56 return os << "CENTER"; 56 return os << "CENTER";
57 case TouchHandleOrientation::UNDEFINED: 57 case TouchHandleOrientation::UNDEFINED:
58 return os << "UNDEFINED"; 58 return os << "UNDEFINED";
59 default: 59 default:
60 return os << "INVALID: " << static_cast<int>(orientation); 60 return os << "INVALID: " << static_cast<int>(orientation);
61 } 61 }
62 } 62 }
63 63
64 // Responsible for rendering a selection or insertion handle for text editing. 64 // Responsible for rendering a selection or insertion handle for text editing.
65 TouchHandle::TouchHandle(TouchHandleClient* client, 65 TouchHandle::TouchHandle(TouchHandleClient* client,
66 TouchHandleOrientation orientation) 66 TouchHandleOrientation orientation,
67 const gfx::RectF& viewport_rect)
67 : drawable_(client->CreateDrawable()), 68 : drawable_(client->CreateDrawable()),
68 client_(client), 69 client_(client),
70 viewport_rect_(viewport_rect),
69 orientation_(orientation), 71 orientation_(orientation),
70 deferred_orientation_(TouchHandleOrientation::UNDEFINED), 72 deferred_orientation_(TouchHandleOrientation::UNDEFINED),
71 alpha_(0.f), 73 alpha_(0.f),
72 animate_deferred_fade_(false), 74 animate_deferred_fade_(false),
73 enabled_(true), 75 enabled_(true),
74 is_visible_(false), 76 is_visible_(false),
75 is_dragging_(false), 77 is_dragging_(false),
76 is_drag_within_tap_region_(false) { 78 is_drag_within_tap_region_(false),
79 is_handle_layout_update_required_(false),
80 mirror_vertical_(false),
81 mirror_horizontal_(false) {
77 DCHECK_NE(orientation, TouchHandleOrientation::UNDEFINED); 82 DCHECK_NE(orientation, TouchHandleOrientation::UNDEFINED);
78 drawable_->SetEnabled(enabled_); 83 drawable_->SetEnabled(enabled_);
79 drawable_->SetOrientation(orientation_); 84 drawable_->SetOrientation(orientation_, false, false);
85 drawable_->SetOrigin(focus_bottom_);
80 drawable_->SetAlpha(alpha_); 86 drawable_->SetAlpha(alpha_);
81 drawable_->SetFocus(position_); 87 handle_horizontal_padding_ = drawable_->GetDrawableHorizontalPaddingRatio();
82 } 88 }
83 89
84 TouchHandle::~TouchHandle() { 90 TouchHandle::~TouchHandle() {
85 } 91 }
86 92
87 void TouchHandle::SetEnabled(bool enabled) { 93 void TouchHandle::SetEnabled(bool enabled) {
88 if (enabled_ == enabled) 94 if (enabled_ == enabled)
89 return; 95 return;
90 if (!enabled) { 96 if (!enabled) {
91 EndDrag(); 97 EndDrag();
92 EndFade(); 98 EndFade();
93 } 99 }
94 enabled_ = enabled; 100 enabled_ = enabled;
95 drawable_->SetEnabled(enabled); 101 drawable_->SetEnabled(enabled);
96 } 102 }
97 103
98 void TouchHandle::SetVisible(bool visible, AnimationStyle animation_style) { 104 void TouchHandle::SetVisible(bool visible, AnimationStyle animation_style) {
99 DCHECK(enabled_); 105 DCHECK(enabled_);
100 if (is_visible_ == visible) 106 if (is_visible_ == visible)
101 return; 107 return;
102 108
103 is_visible_ = visible; 109 is_visible_ = visible;
104 110
105 // Handle repositioning may have been deferred while previously invisible. 111 // Handle repositioning may have been deferred while previously invisible.
106 if (visible) 112 if (visible)
107 drawable_->SetFocus(position_); 113 SetUpdateLayoutRequired();
108 114
109 bool animate = animation_style != ANIMATION_NONE; 115 bool animate = animation_style != ANIMATION_NONE;
110 if (is_dragging_) { 116 if (is_dragging_) {
111 animate_deferred_fade_ = animate; 117 animate_deferred_fade_ = animate;
112 return; 118 return;
113 } 119 }
114 120
115 if (animate) 121 if (animate)
116 BeginFade(); 122 BeginFade();
117 else 123 else
118 EndFade(); 124 EndFade();
119 } 125 }
120 126
121 void TouchHandle::SetPosition(const gfx::PointF& position) { 127 void TouchHandle::SetFocus(const gfx::PointF& top, const gfx::PointF& bottom) {
122 DCHECK(enabled_); 128 DCHECK(enabled_);
123 if (position_ == position) 129 if (focus_top_ == top && focus_bottom_ == bottom)
124 return; 130 return;
125 position_ = position; 131
126 // Suppress repositioning a handle while invisible or fading out to prevent it 132 focus_top_ = top;
127 // from "ghosting" outside the visible bounds. The position will be pushed to 133 focus_bottom_ = bottom;
128 // the drawable when the handle regains visibility (see |SetVisible()|). 134 SetUpdateLayoutRequired();
129 if (is_visible_) 135 }
130 drawable_->SetFocus(position_); 136
137 void TouchHandle::SetViewportRect(const gfx::RectF& viewport_rect) {
138 DCHECK(enabled_);
139 if (viewport_rect_ == viewport_rect)
140 return;
141
142 viewport_rect_ = viewport_rect;
143 SetUpdateLayoutRequired();
131 } 144 }
132 145
133 void TouchHandle::SetOrientation(TouchHandleOrientation orientation) { 146 void TouchHandle::SetOrientation(TouchHandleOrientation orientation) {
134 DCHECK(enabled_); 147 DCHECK(enabled_);
135 DCHECK_NE(orientation, TouchHandleOrientation::UNDEFINED); 148 DCHECK_NE(orientation, TouchHandleOrientation::UNDEFINED);
136 if (is_dragging_) { 149 if (is_dragging_) {
137 deferred_orientation_ = orientation; 150 deferred_orientation_ = orientation;
138 return; 151 return;
139 } 152 }
140 DCHECK_EQ(deferred_orientation_, TouchHandleOrientation::UNDEFINED); 153 DCHECK_EQ(deferred_orientation_, TouchHandleOrientation::UNDEFINED);
141 if (orientation_ == orientation) 154 if (orientation_ == orientation)
142 return; 155 return;
143 156
144 orientation_ = orientation; 157 orientation_ = orientation;
145 drawable_->SetOrientation(orientation); 158 SetUpdateLayoutRequired();
146 } 159 }
147 160
148 bool TouchHandle::WillHandleTouchEvent(const MotionEvent& event) { 161 bool TouchHandle::WillHandleTouchEvent(const MotionEvent& event) {
149 if (!enabled_) 162 if (!enabled_)
150 return false; 163 return false;
151 164
152 if (!is_dragging_ && event.GetAction() != MotionEvent::ACTION_DOWN) 165 if (!is_dragging_ && event.GetAction() != MotionEvent::ACTION_DOWN)
153 return false; 166 return false;
154 167
155 switch (event.GetAction()) { 168 switch (event.GetAction()) {
156 case MotionEvent::ACTION_DOWN: { 169 case MotionEvent::ACTION_DOWN: {
157 if (!is_visible_) 170 if (!is_visible_)
158 return false; 171 return false;
159 const gfx::PointF touch_point(event.GetX(), event.GetY()); 172 const gfx::PointF touch_point(event.GetX(), event.GetY());
160 const float touch_radius = std::max( 173 const float touch_radius = std::max(
161 kMinTouchMajorForHitTesting, 174 kMinTouchMajorForHitTesting,
162 std::min(kMaxTouchMajorForHitTesting, event.GetTouchMajor())) * 0.5f; 175 std::min(kMaxTouchMajorForHitTesting, event.GetTouchMajor())) * 0.5f;
163 const gfx::RectF drawable_bounds = drawable_->GetVisibleBounds(); 176 const gfx::RectF drawable_bounds = drawable_->GetVisibleBounds();
164 // Only use the touch radius for targetting if the touch is at or below 177 // Only use the touch radius for targetting if the touch is at or below
165 // the drawable area. This makes it easier to interact with the line of 178 // the drawable area. This makes it easier to interact with the line of
166 // text above the drawable. 179 // text above the drawable.
167 if (touch_point.y() < drawable_bounds.y() || 180 if (touch_point.y() < drawable_bounds.y() ||
168 !RectIntersectsCircle(drawable_bounds, touch_point, touch_radius)) { 181 !RectIntersectsCircle(drawable_bounds, touch_point, touch_radius)) {
169 EndDrag(); 182 EndDrag();
170 return false; 183 return false;
171 } 184 }
172 touch_down_position_ = touch_point; 185 touch_down_position_ = touch_point;
173 touch_drag_offset_ = position_ - touch_down_position_; 186 touch_drag_offset_ = focus_bottom_ - touch_down_position_;
174 touch_down_time_ = event.GetEventTime(); 187 touch_down_time_ = event.GetEventTime();
175 BeginDrag(); 188 BeginDrag();
176 } break; 189 } break;
177 190
178 case MotionEvent::ACTION_MOVE: { 191 case MotionEvent::ACTION_MOVE: {
179 gfx::PointF touch_move_position(event.GetX(), event.GetY()); 192 gfx::PointF touch_move_position(event.GetX(), event.GetY());
180 is_drag_within_tap_region_ &= 193 is_drag_within_tap_region_ &=
181 client_->IsWithinTapSlop(touch_down_position_ - touch_move_position); 194 client_->IsWithinTapSlop(touch_down_position_ - touch_move_position);
182 195
183 // Note that we signal drag update even if we're inside the tap region, 196 // Note that we signal drag update even if we're inside the tap region,
(...skipping 26 matching lines...) Expand all
210 } 223 }
211 224
212 bool TouchHandle::Animate(base::TimeTicks frame_time) { 225 bool TouchHandle::Animate(base::TimeTicks frame_time) {
213 if (fade_end_time_ == base::TimeTicks()) 226 if (fade_end_time_ == base::TimeTicks())
214 return false; 227 return false;
215 228
216 DCHECK(enabled_); 229 DCHECK(enabled_);
217 230
218 float time_u = 231 float time_u =
219 1.f - (fade_end_time_ - frame_time).InMillisecondsF() / kFadeDurationMs; 232 1.f - (fade_end_time_ - frame_time).InMillisecondsF() / kFadeDurationMs;
220 float position_u = 233 float position_u = (focus_bottom_ - fade_start_position_).LengthSquared() /
221 (position_ - fade_start_position_).LengthSquared() / kFadeDistanceSquared; 234 kFadeDistanceSquared;
222 float u = std::max(time_u, position_u); 235 float u = std::max(time_u, position_u);
223 SetAlpha(is_visible_ ? u : 1.f - u); 236 SetAlpha(is_visible_ ? u : 1.f - u);
224 237
225 if (u >= 1.f) { 238 if (u >= 1.f) {
226 EndFade(); 239 EndFade();
227 return false; 240 return false;
228 } 241 }
229 242
230 return true; 243 return true;
231 } 244 }
232 245
233 gfx::RectF TouchHandle::GetVisibleBounds() const { 246 gfx::RectF TouchHandle::GetVisibleBounds() const {
234 if (!is_visible_ || !enabled_) 247 if (!is_visible_ || !enabled_)
235 return gfx::RectF(); 248 return gfx::RectF();
236 249
237 return drawable_->GetVisibleBounds(); 250 return drawable_->GetVisibleBounds();
238 } 251 }
239 252
253 void TouchHandle::UpdateHandleLayout() {
254 // Suppress repositioning a handle while invisible or fading out to prevent it
255 // from "ghosting" outside the visible bounds. The position will be pushed to
256 // the drawable when the handle regains visibility (see |SetVisible()|).
257 if (!is_visible_ || !is_handle_layout_update_required_)
258 return;
259
260 is_handle_layout_update_required_ = false;
261
262 // Update mirror values only when dragging has stopped to prevent unwanted
263 // inversion while dragging of handles.
264 if (client_->IsAdaptiveHandleOrientationEnabled() && !is_dragging_) {
265 gfx::RectF handle_bounds = drawable_->GetVisibleBounds();
266 bool mirror_horizontal = false;
267 bool mirror_vertical = false;
268
269 const float handle_width =
270 handle_bounds.width() * (1.0 - handle_horizontal_padding_);
271 const float handle_height = handle_bounds.height();
272
273 const float bottom_y_unmirrored =
274 focus_bottom_.y() + handle_height + viewport_rect_.y();
275 const float top_y_mirrored =
276 focus_top_.y() - handle_height + viewport_rect_.y();
277
278 // In case the viewport height is small, like webview, avoid inversion.
279 if (bottom_y_unmirrored > viewport_rect_.bottom() &&
280 top_y_mirrored > viewport_rect_.y()) {
281 mirror_vertical = true;
282 }
283
284 if (orientation_ == TouchHandleOrientation::LEFT &&
285 focus_bottom_.x() - handle_width < viewport_rect_.x()) {
286 mirror_horizontal = true;
287 } else if (orientation_ == TouchHandleOrientation::RIGHT &&
288 focus_bottom_.x() + handle_width > viewport_rect_.right()) {
289 mirror_horizontal = true;
290 }
291
292 mirror_horizontal_ = mirror_horizontal;
293 mirror_vertical_ = mirror_vertical;
294 }
295
296 drawable_->SetOrientation(orientation_, mirror_vertical_, mirror_horizontal_);
297 drawable_->SetOrigin(ComputeHandleOrigin());
298 }
299
300 gfx::PointF TouchHandle::ComputeHandleOrigin() const {
301 gfx::PointF focus = mirror_vertical_ ? focus_top_ : focus_bottom_;
302 gfx::RectF drawable_bounds = drawable_->GetVisibleBounds();
303 float drawable_width = drawable_->GetVisibleBounds().width();
304
305 // Calculate the focal offsets from origin for the handle drawable
306 // based on the orientation.
307 int focal_offset_x = 0;
308 int focal_offset_y = mirror_vertical_ ? drawable_bounds.height() : 0;
309 switch (orientation_) {
310 case ui::TouchHandleOrientation::LEFT:
311 focal_offset_x =
312 mirror_horizontal_
313 ? drawable_width * handle_horizontal_padding_
314 : drawable_width * (1.0f - handle_horizontal_padding_);
315 break;
316 case ui::TouchHandleOrientation::RIGHT:
317 focal_offset_x =
318 mirror_horizontal_
319 ? drawable_width * (1.0f - handle_horizontal_padding_)
320 : drawable_width * handle_horizontal_padding_;
321 break;
322 case ui::TouchHandleOrientation::CENTER:
323 focal_offset_x = drawable_width * 0.5f;
324 break;
325 case ui::TouchHandleOrientation::UNDEFINED:
326 NOTREACHED() << "Invalid touch handle orientation.";
327 break;
328 };
329
330 return focus - gfx::Vector2dF(focal_offset_x, focal_offset_y);
331 }
332
240 void TouchHandle::BeginDrag() { 333 void TouchHandle::BeginDrag() {
241 DCHECK(enabled_); 334 DCHECK(enabled_);
242 if (is_dragging_) 335 if (is_dragging_)
243 return; 336 return;
244 EndFade(); 337 EndFade();
245 is_dragging_ = true; 338 is_dragging_ = true;
246 is_drag_within_tap_region_ = true; 339 is_drag_within_tap_region_ = true;
247 client_->OnDragBegin(*this, position()); 340 client_->OnDragBegin(*this, focus_bottom());
248 } 341 }
249 342
250 void TouchHandle::EndDrag() { 343 void TouchHandle::EndDrag() {
251 DCHECK(enabled_); 344 DCHECK(enabled_);
252 if (!is_dragging_) 345 if (!is_dragging_)
253 return; 346 return;
254 347
255 is_dragging_ = false; 348 is_dragging_ = false;
256 is_drag_within_tap_region_ = false; 349 is_drag_within_tap_region_ = false;
257 client_->OnDragEnd(*this); 350 client_->OnDragEnd(*this);
258 351
259 if (deferred_orientation_ != TouchHandleOrientation::UNDEFINED) { 352 if (deferred_orientation_ != TouchHandleOrientation::UNDEFINED) {
260 TouchHandleOrientation deferred_orientation = deferred_orientation_; 353 TouchHandleOrientation deferred_orientation = deferred_orientation_;
261 deferred_orientation_ = TouchHandleOrientation::UNDEFINED; 354 deferred_orientation_ = TouchHandleOrientation::UNDEFINED;
262 SetOrientation(deferred_orientation); 355 SetOrientation(deferred_orientation);
356 // Handle layout may be deferred while the handle is dragged.
357 SetUpdateLayoutRequired();
358 UpdateHandleLayout();
263 } 359 }
264 360
265 if (animate_deferred_fade_) { 361 if (animate_deferred_fade_) {
266 BeginFade(); 362 BeginFade();
267 } else { 363 } else {
268 // As drawable visibility assignment is deferred while dragging, push the 364 // As drawable visibility assignment is deferred while dragging, push the
269 // change by forcing fade completion. 365 // change by forcing fade completion.
270 EndFade(); 366 EndFade();
271 } 367 }
272 } 368 }
273 369
274 void TouchHandle::BeginFade() { 370 void TouchHandle::BeginFade() {
275 DCHECK(enabled_); 371 DCHECK(enabled_);
276 DCHECK(!is_dragging_); 372 DCHECK(!is_dragging_);
277 animate_deferred_fade_ = false; 373 animate_deferred_fade_ = false;
278 const float target_alpha = is_visible_ ? 1.f : 0.f; 374 const float target_alpha = is_visible_ ? 1.f : 0.f;
279 if (target_alpha == alpha_) { 375 if (target_alpha == alpha_) {
280 EndFade(); 376 EndFade();
281 return; 377 return;
282 } 378 }
283 379
284 fade_end_time_ = base::TimeTicks::Now() + 380 fade_end_time_ = base::TimeTicks::Now() +
285 base::TimeDelta::FromMillisecondsD( 381 base::TimeDelta::FromMillisecondsD(
286 kFadeDurationMs * std::abs(target_alpha - alpha_)); 382 kFadeDurationMs * std::abs(target_alpha - alpha_));
287 fade_start_position_ = position_; 383 fade_start_position_ = focus_bottom_;
288 client_->SetNeedsAnimate(); 384 client_->SetNeedsAnimate();
289 } 385 }
290 386
291 void TouchHandle::EndFade() { 387 void TouchHandle::EndFade() {
292 DCHECK(enabled_); 388 DCHECK(enabled_);
293 animate_deferred_fade_ = false; 389 animate_deferred_fade_ = false;
294 fade_end_time_ = base::TimeTicks(); 390 fade_end_time_ = base::TimeTicks();
295 SetAlpha(is_visible_ ? 1.f : 0.f); 391 SetAlpha(is_visible_ ? 1.f : 0.f);
296 } 392 }
297 393
298 void TouchHandle::SetAlpha(float alpha) { 394 void TouchHandle::SetAlpha(float alpha) {
299 alpha = std::max(0.f, std::min(1.f, alpha)); 395 alpha = std::max(0.f, std::min(1.f, alpha));
300 if (alpha_ == alpha) 396 if (alpha_ == alpha)
301 return; 397 return;
302 alpha_ = alpha; 398 alpha_ = alpha;
303 drawable_->SetAlpha(alpha); 399 drawable_->SetAlpha(alpha);
304 } 400 }
305 401
402 void TouchHandle::SetUpdateLayoutRequired() {
403 // TODO(AviD): Make the layout call explicit to the caller by adding this in
404 // TouchHandleClient.
405 is_handle_layout_update_required_ = true;
406 }
407
306 } // namespace ui 408 } // namespace ui
OLDNEW
« no previous file with comments | « ui/touch_selection/touch_handle.h ('k') | ui/touch_selection/touch_handle_drawable_aura.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698