OLD | NEW |
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_selection_controller.h" | 5 #include "ui/touch_selection/touch_selection_controller.h" |
6 | 6 |
7 #include "base/auto_reset.h" | 7 #include "base/auto_reset.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 | 9 |
10 namespace ui { | 10 namespace ui { |
(...skipping 21 matching lines...) Expand all Loading... |
32 return TOUCH_HANDLE_CENTER; | 32 return TOUCH_HANDLE_CENTER; |
33 case SelectionBound::EMPTY: | 33 case SelectionBound::EMPTY: |
34 return TOUCH_HANDLE_ORIENTATION_UNDEFINED; | 34 return TOUCH_HANDLE_ORIENTATION_UNDEFINED; |
35 } | 35 } |
36 NOTREACHED() << "Invalid selection bound type: " << type; | 36 NOTREACHED() << "Invalid selection bound type: " << type; |
37 return TOUCH_HANDLE_ORIENTATION_UNDEFINED; | 37 return TOUCH_HANDLE_ORIENTATION_UNDEFINED; |
38 } | 38 } |
39 | 39 |
40 } // namespace | 40 } // namespace |
41 | 41 |
| 42 class GranularityStrategy { |
| 43 public: |
| 44 virtual ~GranularityStrategy() {}; |
| 45 virtual void OnSelectionBoundsUpdated(const SelectionBound& start, |
| 46 const SelectionBound& end) {}; |
| 47 virtual void OnHandleDragBegin(const gfx::PointF& position, |
| 48 base::TimeTicks event_time, |
| 49 TouchSelectionController* controller) {}; |
| 50 virtual TextSelectionGranularity OnHandleDragUpdate( |
| 51 const gfx::PointF& position, |
| 52 const gfx::PointF& line_position, |
| 53 base::TimeTicks event_time, |
| 54 bool end_handle_dragged) { return ui::CHARACTER_GRANULARITY; }; |
| 55 protected: |
| 56 GranularityStrategy() {}; |
| 57 }; |
| 58 |
| 59 class DefaultGranularityStrategy : public GranularityStrategy { |
| 60 public: |
| 61 DefaultGranularityStrategy() {}; |
| 62 ~DefaultGranularityStrategy() override {}; |
| 63 }; |
| 64 |
| 65 class DirectionGranularityStrategy : public GranularityStrategy { |
| 66 public: |
| 67 DirectionGranularityStrategy() : granularity_(ui::CHARACTER_GRANULARITY) {}; |
| 68 |
| 69 ~DirectionGranularityStrategy() override {}; |
| 70 |
| 71 void OnSelectionBoundsUpdated(const SelectionBound& start, |
| 72 const SelectionBound& end) override { |
| 73 // LOG(ERROR) << "Bounds updated: " |
| 74 // << "start.y/2=" << (start.edge_bottom_rounded().y() + start.edge_top_r
ounded().y()) /2 |
| 75 // << ", start.x=" << start.edge_bottom_rounded().x() |
| 76 // << ", end.y/2=" << (end.edge_bottom_rounded().y() + end.edge_top_round
ed().y()) / 2 |
| 77 // << ", end.x=" << end.edge_bottom_rounded().x(); |
| 78 |
| 79 if (granularity_ == ui::WORD_GRANULARITY) { |
| 80 granularity_threshold_start_ = start; |
| 81 granularity_threshold_end_ = end; |
| 82 } |
| 83 }; |
| 84 |
| 85 void OnHandleDragBegin(const gfx::PointF& position, |
| 86 base::TimeTicks event_time, |
| 87 TouchSelectionController* controller) override { |
| 88 // TODO(mfomitchev): |
| 89 // Ideally we want word boundaries communicated in CompositorFrameMetadata |
| 90 // (see RenderWidgetHostViewAura::OnSwapCompositorFrame) |
| 91 granularity_threshold_start_ = controller->start(); |
| 92 granularity_threshold_end_ = controller->end(); |
| 93 farthest_dragging_handle_position_ = position; |
| 94 granularity_ = ui::WORD_GRANULARITY; |
| 95 }; |
| 96 |
| 97 // TODO(mfomitchev): perhaps this should depend on character size? |
| 98 // tap_slop_ is too big, uses max_touch_move_in_pixels_for_click. Perhaps |
| 99 // we should use span_slop? See GestureConfiguration |
| 100 const int kHandleDragSlop = 7; |
| 101 TextSelectionGranularity OnHandleDragUpdate( |
| 102 const gfx::PointF& position, |
| 103 const gfx::PointF& line_position, |
| 104 base::TimeTicks event_time, |
| 105 bool end_handle_dragged) override { |
| 106 if (end_handle_dragged) { |
| 107 if (line_position.y() > granularity_threshold_end_.edge_bottom().y()) { |
| 108 LOG(ERROR) << "Moved down a line - adjusting max_dragging_handle_positio
n_"; |
| 109 farthest_dragging_handle_position_ = position; |
| 110 if (granularity_ == ui::CHARACTER_GRANULARITY) { |
| 111 LOG(ERROR) << "Moved down a line, switching to WORD_GRANULARITY"; |
| 112 granularity_ = ui::WORD_GRANULARITY; |
| 113 } |
| 114 } else if (line_position.y() < granularity_threshold_end_.edge_top().y())
{ |
| 115 LOG(ERROR) << "Moved up a line - adjusting max_dragging_handle_position_
"; |
| 116 farthest_dragging_handle_position_ = position; |
| 117 if (granularity_ == ui::CHARACTER_GRANULARITY) { |
| 118 granularity_ = ui::WORD_GRANULARITY; |
| 119 LOG(ERROR) << "Moved up a line - switching to WORD_GRANULARITY"; |
| 120 } |
| 121 } else if (position.x() > farthest_dragging_handle_position_.x()) { |
| 122 LOG(ERROR) << "Incrementing max_dragging_handle_position_"; |
| 123 farthest_dragging_handle_position_ = position; |
| 124 if (granularity_ == ui::CHARACTER_GRANULARITY && |
| 125 position.x() > granularity_threshold_end_.edge_bottom().x()) { |
| 126 granularity_ = ui::WORD_GRANULARITY; |
| 127 LOG(ERROR) << "Moved right of granularity_threshold_end_," |
| 128 << " switching to WORD_GRANULARITY"; |
| 129 } |
| 130 } else if (position.x() + kHandleDragSlop < |
| 131 farthest_dragging_handle_position_.x()) { |
| 132 if (granularity_ == ui::WORD_GRANULARITY) { |
| 133 granularity_ = ui::CHARACTER_GRANULARITY; |
| 134 LOG(ERROR) << "Moved left of max_dragging_handle_position_," |
| 135 << " switching to CHARACTER_GRANULARITY"; |
| 136 } |
| 137 } |
| 138 } else { //!end_handle_dragged |
| 139 if (line_position.y() < granularity_threshold_start_.edge_top().y() || |
| 140 line_position.y() > granularity_threshold_start_.edge_bottom().y()) { |
| 141 // Line change |
| 142 farthest_dragging_handle_position_ = position; |
| 143 granularity_ = ui::WORD_GRANULARITY; |
| 144 } else if (position.x() < farthest_dragging_handle_position_.x()) { |
| 145 farthest_dragging_handle_position_ = position; |
| 146 if (position.x() < granularity_threshold_start_.edge_bottom().x()) |
| 147 granularity_ = ui::WORD_GRANULARITY; |
| 148 } else if (position.x() - kHandleDragSlop > |
| 149 farthest_dragging_handle_position_.x()) { |
| 150 granularity_ = ui::CHARACTER_GRANULARITY; |
| 151 } |
| 152 } |
| 153 return granularity_; |
| 154 } |
| 155 |
| 156 private: |
| 157 TextSelectionGranularity granularity_; |
| 158 SelectionBound granularity_threshold_start_; |
| 159 SelectionBound granularity_threshold_end_; |
| 160 // If we go left of this by more than slop - switch to CHAR granularity |
| 161 // For right handle - farthest down/right |
| 162 // For left handle - farthest up/left |
| 163 gfx::PointF farthest_dragging_handle_position_; |
| 164 |
| 165 DISALLOW_COPY_AND_ASSIGN(DirectionGranularityStrategy); |
| 166 }; |
| 167 |
| 168 class VelocityGranularityStrategy : public GranularityStrategy { |
| 169 public: |
| 170 VelocityGranularityStrategy() : |
| 171 granularity_(ui::CHARACTER_GRANULARITY), |
| 172 avg_velocity_(0.f), |
| 173 filterTimeConstant_(kFilterTimeConstant), |
| 174 velocityGranularityThreshold_(kVelocityGranularityThreshold) {}; |
| 175 |
| 176 ~VelocityGranularityStrategy() override {}; |
| 177 |
| 178 const float kFilterTimeConstant = 0.3f; // half decay @ 300ms |
| 179 const float kVelocityGranularityThreshold = 80.f; |
| 180 |
| 181 void SetStrategyParameters(int halfDecayMs, int threshold) { |
| 182 filterTimeConstant_ = halfDecayMs / 1000.f; |
| 183 velocityGranularityThreshold_ = threshold; |
| 184 } |
| 185 |
| 186 void OnHandleDragBegin(const gfx::PointF& position, |
| 187 base::TimeTicks event_time, |
| 188 TouchSelectionController* controller) override { |
| 189 last_dragging_handle_position_ = position; |
| 190 avg_velocity_ = 0; |
| 191 last_event_time_ = event_time; |
| 192 granularity_ = ui::WORD_GRANULARITY; |
| 193 }; |
| 194 |
| 195 TextSelectionGranularity OnHandleDragUpdate( |
| 196 const gfx::PointF& position, |
| 197 const gfx::PointF& line_position, |
| 198 base::TimeTicks event_time, |
| 199 bool end_handle_dragged) override { |
| 200 float dist = (position - last_dragging_handle_position_).Length(); |
| 201 float dt = static_cast<float>((event_time - last_event_time_).InSecondsF()); |
| 202 float current_velocity = dist / dt; |
| 203 |
| 204 if (avg_velocity_ == 0) { |
| 205 avg_velocity_ = current_velocity; |
| 206 } else { |
| 207 float alpha = dt / (filterTimeConstant_ + dt); |
| 208 avg_velocity_ += (current_velocity - avg_velocity_) * alpha; |
| 209 } |
| 210 |
| 211 if (avg_velocity_ > velocityGranularityThreshold_) |
| 212 granularity_ = ui::WORD_GRANULARITY; |
| 213 else |
| 214 granularity_ = ui::CHARACTER_GRANULARITY; |
| 215 |
| 216 LOG(ERROR) << "avg_velocity=" << avg_velocity_ |
| 217 << ", current_velocity=" << current_velocity; |
| 218 |
| 219 last_dragging_handle_position_ = position; |
| 220 last_event_time_ = event_time; |
| 221 |
| 222 return granularity_; |
| 223 }; |
| 224 |
| 225 private: |
| 226 TextSelectionGranularity granularity_; |
| 227 gfx::PointF last_dragging_handle_position_; |
| 228 base::TimeTicks last_event_time_; |
| 229 float avg_velocity_; |
| 230 |
| 231 float filterTimeConstant_; |
| 232 float velocityGranularityThreshold_; |
| 233 |
| 234 |
| 235 DISALLOW_COPY_AND_ASSIGN(VelocityGranularityStrategy); |
| 236 }; |
| 237 |
42 TouchSelectionController::TouchSelectionController( | 238 TouchSelectionController::TouchSelectionController( |
43 TouchSelectionControllerClient* client, | 239 TouchSelectionControllerClient* client, |
44 base::TimeDelta tap_timeout, | 240 base::TimeDelta tap_timeout, |
45 float tap_slop, | 241 float tap_slop, |
46 bool show_on_tap_for_empty_editable) | 242 bool show_on_tap_for_empty_editable, |
| 243 TextSelectionGranularityStrategy selection_granularity_strategy) |
47 : client_(client), | 244 : client_(client), |
48 tap_timeout_(tap_timeout), | 245 tap_timeout_(tap_timeout), |
49 tap_slop_(tap_slop), | 246 tap_slop_(tap_slop), |
50 show_on_tap_for_empty_editable_(show_on_tap_for_empty_editable), | 247 show_on_tap_for_empty_editable_(show_on_tap_for_empty_editable), |
51 response_pending_input_event_(INPUT_EVENT_TYPE_NONE), | 248 response_pending_input_event_(INPUT_EVENT_TYPE_NONE), |
52 start_orientation_(TOUCH_HANDLE_ORIENTATION_UNDEFINED), | 249 start_orientation_(TOUCH_HANDLE_ORIENTATION_UNDEFINED), |
53 end_orientation_(TOUCH_HANDLE_ORIENTATION_UNDEFINED), | 250 end_orientation_(TOUCH_HANDLE_ORIENTATION_UNDEFINED), |
54 is_insertion_active_(false), | 251 is_insertion_active_(false), |
55 activate_insertion_automatically_(false), | 252 activate_insertion_automatically_(false), |
56 is_selection_active_(false), | 253 is_selection_active_(false), |
57 activate_selection_automatically_(false), | 254 activate_selection_automatically_(false), |
58 selection_empty_(false), | 255 selection_empty_(false), |
59 selection_editable_(false), | 256 selection_editable_(false), |
60 temporarily_hidden_(false) { | 257 temporarily_hidden_(false), |
| 258 selection_granularity_strategy_(selection_granularity_strategy) { |
61 DCHECK(client_); | 259 DCHECK(client_); |
| 260 switch (selection_granularity_strategy) { |
| 261 case GRANULARITY_STRATEGY_DEFAULT: |
| 262 granularity_strategy_.reset(new DefaultGranularityStrategy()); |
| 263 break; |
| 264 case GRANULARITY_STRATEGY_DIRECTION: |
| 265 granularity_strategy_.reset(new DirectionGranularityStrategy()); |
| 266 break; |
| 267 case GRANULARITY_STRATEGY_VELOCITY: |
| 268 granularity_strategy_.reset(new VelocityGranularityStrategy()); |
| 269 break; |
| 270 } |
62 } | 271 } |
63 | 272 |
64 TouchSelectionController::~TouchSelectionController() { | 273 TouchSelectionController::~TouchSelectionController() { |
65 } | 274 } |
66 | 275 |
| 276 void TouchSelectionController::SetVelocityStrategyParameters(int halfDecayMs, |
| 277 int threshold) { |
| 278 if (selection_granularity_strategy_ != GRANULARITY_STRATEGY_VELOCITY) |
| 279 return; |
| 280 |
| 281 VelocityGranularityStrategy* velocityStrategy = |
| 282 static_cast<VelocityGranularityStrategy*>(granularity_strategy_.get()); |
| 283 velocityStrategy->SetStrategyParameters(halfDecayMs, threshold); |
| 284 } |
| 285 |
67 void TouchSelectionController::OnSelectionBoundsUpdated( | 286 void TouchSelectionController::OnSelectionBoundsUpdated( |
68 const SelectionBound& start, | 287 const SelectionBound& start, |
69 const SelectionBound& end) { | 288 const SelectionBound& end) { |
70 if (start == start_ && end_ == end) | 289 if (start == start_ && end_ == end) |
71 return; | 290 return; |
72 | 291 |
| 292 granularity_strategy_->OnSelectionBoundsUpdated(start, end); |
| 293 |
73 start_ = start; | 294 start_ = start; |
74 end_ = end; | 295 end_ = end; |
75 start_orientation_ = ToTouchHandleOrientation(start_.type()); | 296 start_orientation_ = ToTouchHandleOrientation(start_.type()); |
76 end_orientation_ = ToTouchHandleOrientation(end_.type()); | 297 end_orientation_ = ToTouchHandleOrientation(end_.type()); |
77 | 298 |
78 if (!activate_selection_automatically_ && | 299 if (!activate_selection_automatically_ && |
79 !activate_insertion_automatically_) { | 300 !activate_insertion_automatically_) { |
80 DCHECK_EQ(INPUT_EVENT_TYPE_NONE, response_pending_input_event_); | 301 DCHECK_EQ(INPUT_EVENT_TYPE_NONE, response_pending_input_event_); |
81 return; | 302 return; |
82 } | 303 } |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 | 449 |
229 void TouchSelectionController::TryActivateSelection() { | 450 void TouchSelectionController::TryActivateSelection() { |
230 if (GetStartPosition() != GetEndPosition()) { | 451 if (GetStartPosition() != GetEndPosition()) { |
231 SelectionBound start = start_; | 452 SelectionBound start = start_; |
232 SelectionBound end = end_; | 453 SelectionBound end = end_; |
233 ShowSelectionHandlesAutomatically(); | 454 ShowSelectionHandlesAutomatically(); |
234 OnSelectionBoundsUpdated(start, end); | 455 OnSelectionBoundsUpdated(start, end); |
235 } | 456 } |
236 } | 457 } |
237 | 458 |
238 void TouchSelectionController::OnHandleDragBegin(const TouchHandle& handle) { | 459 void TouchSelectionController::OnHandleDragBegin(const TouchHandle& handle, |
| 460 base::TimeTicks event_time) { |
239 if (&handle == insertion_handle_.get()) { | 461 if (&handle == insertion_handle_.get()) { |
240 client_->OnSelectionEvent(INSERTION_DRAG_STARTED, handle.position()); | 462 client_->OnSelectionEvent(INSERTION_DRAG_STARTED, handle.position()); |
241 return; | 463 return; |
242 } | 464 } |
243 | 465 |
244 gfx::PointF base, extent; | 466 gfx::PointF base, extent; |
245 if (&handle == start_selection_handle_.get()) { | 467 if (&handle == start_selection_handle_.get()) { |
246 base = end_selection_handle_->position() + GetEndLineOffset(); | 468 base = end_selection_handle_->position() + GetEndLineOffset(); |
247 extent = start_selection_handle_->position() + GetStartLineOffset(); | 469 extent = start_selection_handle_->position() + GetStartLineOffset(); |
| 470 // TODO: HACK |
| 471 client_->SelectBetweenCoordinates(base, start_.edge_bottom_rounded() + GetSt
artLineOffset()); |
248 } else { | 472 } else { |
249 base = start_selection_handle_->position() + GetStartLineOffset(); | 473 base = start_selection_handle_->position() + GetStartLineOffset(); |
250 extent = end_selection_handle_->position() + GetEndLineOffset(); | 474 extent = end_selection_handle_->position() + GetEndLineOffset(); |
| 475 // TODO: HACK |
| 476 client_->SelectBetweenCoordinates(base, end_.edge_bottom_rounded() + GetEndL
ineOffset()); |
251 } | 477 } |
252 | 478 |
| 479 granularity_strategy_->OnHandleDragBegin(handle.position(), event_time, this); |
| 480 |
253 // When moving the handle we want to move only the extent point. Before doing | 481 // When moving the handle we want to move only the extent point. Before doing |
254 // so we must make sure that the base point is set correctly. | 482 // so we must make sure that the base point is set correctly. |
255 client_->SelectBetweenCoordinates(base, extent); | 483 // TODO: HACK - swallow the update |
| 484 //client_->SelectBetweenCoordinates(base, extent); |
| 485 //OnHandleDragUpdate(handle, handle.position()); |
256 | 486 |
257 client_->OnSelectionEvent(SELECTION_DRAG_STARTED, handle.position()); | 487 client_->OnSelectionEvent(SELECTION_DRAG_STARTED, handle.position()); |
258 } | 488 } |
259 | 489 |
260 void TouchSelectionController::OnHandleDragUpdate(const TouchHandle& handle, | 490 void TouchSelectionController::OnHandleDragUpdate(const TouchHandle& handle, |
261 const gfx::PointF& position) { | 491 const gfx::PointF& position, |
| 492 base::TimeTicks event_time) { |
| 493 // TODO: figure out RTL |
| 494 // TODO: start/end can switch! Handle! |
| 495 bool end_handle_dragged = &handle == end_selection_handle_.get(); |
262 // As the position corresponds to the bottom left point of the selection | 496 // As the position corresponds to the bottom left point of the selection |
263 // bound, offset it by half the corresponding line height. | 497 // bound, offset it by half the corresponding line height. |
264 gfx::Vector2dF line_offset = &handle == start_selection_handle_.get() | 498 gfx::Vector2dF line_offset = end_handle_dragged |
265 ? GetStartLineOffset() | 499 ? GetEndLineOffset() |
266 : GetEndLineOffset(); | 500 : GetStartLineOffset(); |
| 501 |
267 gfx::PointF line_position = position + line_offset; | 502 gfx::PointF line_position = position + line_offset; |
| 503 |
268 if (&handle == insertion_handle_.get()) { | 504 if (&handle == insertion_handle_.get()) { |
269 client_->MoveCaret(line_position); | 505 client_->MoveCaret(line_position); |
270 } else { | 506 } else { |
271 client_->MoveRangeSelectionExtent(line_position, ui::CHARACTER_GRANULARITY); | 507 TextSelectionGranularity granularity = |
| 508 granularity_strategy_->OnHandleDragUpdate(position, |
| 509 line_position, |
| 510 event_time, |
| 511 end_handle_dragged); |
| 512 client_->MoveRangeSelectionExtent(line_position, granularity); |
272 } | 513 } |
273 } | 514 } |
274 | 515 |
275 void TouchSelectionController::OnHandleDragEnd(const TouchHandle& handle) { | 516 void TouchSelectionController::OnHandleDragEnd(const TouchHandle& handle) { |
276 if (&handle == insertion_handle_.get()) | 517 if (&handle == insertion_handle_.get()) |
277 client_->OnSelectionEvent(INSERTION_DRAG_STOPPED, handle.position()); | 518 client_->OnSelectionEvent(INSERTION_DRAG_STOPPED, handle.position()); |
278 else | 519 else |
279 client_->OnSelectionEvent(SELECTION_DRAG_STOPPED, handle.position()); | 520 client_->OnSelectionEvent(SELECTION_DRAG_STOPPED, handle.position()); |
280 } | 521 } |
281 | 522 |
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
449 } | 690 } |
450 | 691 |
451 TouchHandle::AnimationStyle TouchSelectionController::GetAnimationStyle( | 692 TouchHandle::AnimationStyle TouchSelectionController::GetAnimationStyle( |
452 bool was_active) const { | 693 bool was_active) const { |
453 return was_active && client_->SupportsAnimation() | 694 return was_active && client_->SupportsAnimation() |
454 ? TouchHandle::ANIMATION_SMOOTH | 695 ? TouchHandle::ANIMATION_SMOOTH |
455 : TouchHandle::ANIMATION_NONE; | 696 : TouchHandle::ANIMATION_NONE; |
456 } | 697 } |
457 | 698 |
458 } // namespace ui | 699 } // namespace ui |
OLD | NEW |