| Index: ui/touch_selection/touch_selection_controller.cc
|
| diff --git a/ui/touch_selection/touch_selection_controller.cc b/ui/touch_selection/touch_selection_controller.cc
|
| index ec720b4280865ebdf66a4c1e49b4c73fa7b5324e..dbc7e5e47152df6b323f339c79ca4fa5ba3fb0a9 100644
|
| --- a/ui/touch_selection/touch_selection_controller.cc
|
| +++ b/ui/touch_selection/touch_selection_controller.cc
|
| @@ -39,11 +39,208 @@ TouchHandleOrientation ToTouchHandleOrientation(SelectionBound::Type type) {
|
|
|
| } // namespace
|
|
|
| +class GranularityStrategy {
|
| + public:
|
| + virtual ~GranularityStrategy() {};
|
| + virtual void OnSelectionBoundsUpdated(const SelectionBound& start,
|
| + const SelectionBound& end) {};
|
| + virtual void OnHandleDragBegin(const gfx::PointF& position,
|
| + base::TimeTicks event_time,
|
| + TouchSelectionController* controller) {};
|
| + virtual TextSelectionGranularity OnHandleDragUpdate(
|
| + const gfx::PointF& position,
|
| + const gfx::PointF& line_position,
|
| + base::TimeTicks event_time,
|
| + bool end_handle_dragged) { return ui::CHARACTER_GRANULARITY; };
|
| + protected:
|
| + GranularityStrategy() {};
|
| +};
|
| +
|
| +class DefaultGranularityStrategy : public GranularityStrategy {
|
| + public:
|
| + DefaultGranularityStrategy() {};
|
| + ~DefaultGranularityStrategy() override {};
|
| +};
|
| +
|
| +class DirectionGranularityStrategy : public GranularityStrategy {
|
| + public:
|
| + DirectionGranularityStrategy() : granularity_(ui::CHARACTER_GRANULARITY) {};
|
| +
|
| + ~DirectionGranularityStrategy() override {};
|
| +
|
| + void OnSelectionBoundsUpdated(const SelectionBound& start,
|
| + const SelectionBound& end) override {
|
| +// LOG(ERROR) << "Bounds updated: "
|
| +// << "start.y/2=" << (start.edge_bottom_rounded().y() + start.edge_top_rounded().y()) /2
|
| +// << ", start.x=" << start.edge_bottom_rounded().x()
|
| +// << ", end.y/2=" << (end.edge_bottom_rounded().y() + end.edge_top_rounded().y()) / 2
|
| +// << ", end.x=" << end.edge_bottom_rounded().x();
|
| +
|
| + if (granularity_ == ui::WORD_GRANULARITY) {
|
| + granularity_threshold_start_ = start;
|
| + granularity_threshold_end_ = end;
|
| + }
|
| + };
|
| +
|
| + void OnHandleDragBegin(const gfx::PointF& position,
|
| + base::TimeTicks event_time,
|
| + TouchSelectionController* controller) override {
|
| + // TODO(mfomitchev):
|
| + // Ideally we want word boundaries communicated in CompositorFrameMetadata
|
| + // (see RenderWidgetHostViewAura::OnSwapCompositorFrame)
|
| + granularity_threshold_start_ = controller->start();
|
| + granularity_threshold_end_ = controller->end();
|
| + farthest_dragging_handle_position_ = position;
|
| + granularity_ = ui::WORD_GRANULARITY;
|
| + };
|
| +
|
| + // TODO(mfomitchev): perhaps this should depend on character size?
|
| + // tap_slop_ is too big, uses max_touch_move_in_pixels_for_click. Perhaps
|
| + // we should use span_slop? See GestureConfiguration
|
| + const int kHandleDragSlop = 7;
|
| + TextSelectionGranularity OnHandleDragUpdate(
|
| + const gfx::PointF& position,
|
| + const gfx::PointF& line_position,
|
| + base::TimeTicks event_time,
|
| + bool end_handle_dragged) override {
|
| + if (end_handle_dragged) {
|
| + if (line_position.y() > granularity_threshold_end_.edge_bottom().y()) {
|
| + LOG(ERROR) << "Moved down a line - adjusting max_dragging_handle_position_";
|
| + farthest_dragging_handle_position_ = position;
|
| + if (granularity_ == ui::CHARACTER_GRANULARITY) {
|
| + LOG(ERROR) << "Moved down a line, switching to WORD_GRANULARITY";
|
| + granularity_ = ui::WORD_GRANULARITY;
|
| + }
|
| + } else if (line_position.y() < granularity_threshold_end_.edge_top().y()) {
|
| + LOG(ERROR) << "Moved up a line - adjusting max_dragging_handle_position_";
|
| + farthest_dragging_handle_position_ = position;
|
| + if (granularity_ == ui::CHARACTER_GRANULARITY) {
|
| + granularity_ = ui::WORD_GRANULARITY;
|
| + LOG(ERROR) << "Moved up a line - switching to WORD_GRANULARITY";
|
| + }
|
| + } else if (position.x() > farthest_dragging_handle_position_.x()) {
|
| + LOG(ERROR) << "Incrementing max_dragging_handle_position_";
|
| + farthest_dragging_handle_position_ = position;
|
| + if (granularity_ == ui::CHARACTER_GRANULARITY &&
|
| + position.x() > granularity_threshold_end_.edge_bottom().x()) {
|
| + granularity_ = ui::WORD_GRANULARITY;
|
| + LOG(ERROR) << "Moved right of granularity_threshold_end_,"
|
| + << " switching to WORD_GRANULARITY";
|
| + }
|
| + } else if (position.x() + kHandleDragSlop <
|
| + farthest_dragging_handle_position_.x()) {
|
| + if (granularity_ == ui::WORD_GRANULARITY) {
|
| + granularity_ = ui::CHARACTER_GRANULARITY;
|
| + LOG(ERROR) << "Moved left of max_dragging_handle_position_,"
|
| + << " switching to CHARACTER_GRANULARITY";
|
| + }
|
| + }
|
| + } else { //!end_handle_dragged
|
| + if (line_position.y() < granularity_threshold_start_.edge_top().y() ||
|
| + line_position.y() > granularity_threshold_start_.edge_bottom().y()) {
|
| + // Line change
|
| + farthest_dragging_handle_position_ = position;
|
| + granularity_ = ui::WORD_GRANULARITY;
|
| + } else if (position.x() < farthest_dragging_handle_position_.x()) {
|
| + farthest_dragging_handle_position_ = position;
|
| + if (position.x() < granularity_threshold_start_.edge_bottom().x())
|
| + granularity_ = ui::WORD_GRANULARITY;
|
| + } else if (position.x() - kHandleDragSlop >
|
| + farthest_dragging_handle_position_.x()) {
|
| + granularity_ = ui::CHARACTER_GRANULARITY;
|
| + }
|
| + }
|
| + return granularity_;
|
| + }
|
| +
|
| + private:
|
| + TextSelectionGranularity granularity_;
|
| + SelectionBound granularity_threshold_start_;
|
| + SelectionBound granularity_threshold_end_;
|
| + // If we go left of this by more than slop - switch to CHAR granularity
|
| + // For right handle - farthest down/right
|
| + // For left handle - farthest up/left
|
| + gfx::PointF farthest_dragging_handle_position_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(DirectionGranularityStrategy);
|
| +};
|
| +
|
| +class VelocityGranularityStrategy : public GranularityStrategy {
|
| + public:
|
| + VelocityGranularityStrategy() :
|
| + granularity_(ui::CHARACTER_GRANULARITY),
|
| + avg_velocity_(0.f),
|
| + filterTimeConstant_(kFilterTimeConstant),
|
| + velocityGranularityThreshold_(kVelocityGranularityThreshold) {};
|
| +
|
| + ~VelocityGranularityStrategy() override {};
|
| +
|
| + const float kFilterTimeConstant = 0.3f; // half decay @ 300ms
|
| + const float kVelocityGranularityThreshold = 80.f;
|
| +
|
| + void SetStrategyParameters(int halfDecayMs, int threshold) {
|
| + filterTimeConstant_ = halfDecayMs / 1000.f;
|
| + velocityGranularityThreshold_ = threshold;
|
| + }
|
| +
|
| + void OnHandleDragBegin(const gfx::PointF& position,
|
| + base::TimeTicks event_time,
|
| + TouchSelectionController* controller) override {
|
| + last_dragging_handle_position_ = position;
|
| + avg_velocity_ = 0;
|
| + last_event_time_ = event_time;
|
| + granularity_ = ui::WORD_GRANULARITY;
|
| + };
|
| +
|
| + TextSelectionGranularity OnHandleDragUpdate(
|
| + const gfx::PointF& position,
|
| + const gfx::PointF& line_position,
|
| + base::TimeTicks event_time,
|
| + bool end_handle_dragged) override {
|
| + float dist = (position - last_dragging_handle_position_).Length();
|
| + float dt = static_cast<float>((event_time - last_event_time_).InSecondsF());
|
| + float current_velocity = dist / dt;
|
| +
|
| + if (avg_velocity_ == 0) {
|
| + avg_velocity_ = current_velocity;
|
| + } else {
|
| + float alpha = dt / (filterTimeConstant_ + dt);
|
| + avg_velocity_ += (current_velocity - avg_velocity_) * alpha;
|
| + }
|
| +
|
| + if (avg_velocity_ > velocityGranularityThreshold_)
|
| + granularity_ = ui::WORD_GRANULARITY;
|
| + else
|
| + granularity_ = ui::CHARACTER_GRANULARITY;
|
| +
|
| + LOG(ERROR) << "avg_velocity=" << avg_velocity_
|
| + << ", current_velocity=" << current_velocity;
|
| +
|
| + last_dragging_handle_position_ = position;
|
| + last_event_time_ = event_time;
|
| +
|
| + return granularity_;
|
| + };
|
| +
|
| + private:
|
| + TextSelectionGranularity granularity_;
|
| + gfx::PointF last_dragging_handle_position_;
|
| + base::TimeTicks last_event_time_;
|
| + float avg_velocity_;
|
| +
|
| + float filterTimeConstant_;
|
| + float velocityGranularityThreshold_;
|
| +
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(VelocityGranularityStrategy);
|
| +};
|
| +
|
| TouchSelectionController::TouchSelectionController(
|
| TouchSelectionControllerClient* client,
|
| base::TimeDelta tap_timeout,
|
| float tap_slop,
|
| - bool show_on_tap_for_empty_editable)
|
| + bool show_on_tap_for_empty_editable,
|
| + TextSelectionGranularityStrategy selection_granularity_strategy)
|
| : client_(client),
|
| tap_timeout_(tap_timeout),
|
| tap_slop_(tap_slop),
|
| @@ -57,19 +254,43 @@ TouchSelectionController::TouchSelectionController(
|
| activate_selection_automatically_(false),
|
| selection_empty_(false),
|
| selection_editable_(false),
|
| - temporarily_hidden_(false) {
|
| + temporarily_hidden_(false),
|
| + selection_granularity_strategy_(selection_granularity_strategy) {
|
| DCHECK(client_);
|
| + switch (selection_granularity_strategy) {
|
| + case GRANULARITY_STRATEGY_DEFAULT:
|
| + granularity_strategy_.reset(new DefaultGranularityStrategy());
|
| + break;
|
| + case GRANULARITY_STRATEGY_DIRECTION:
|
| + granularity_strategy_.reset(new DirectionGranularityStrategy());
|
| + break;
|
| + case GRANULARITY_STRATEGY_VELOCITY:
|
| + granularity_strategy_.reset(new VelocityGranularityStrategy());
|
| + break;
|
| + }
|
| }
|
|
|
| TouchSelectionController::~TouchSelectionController() {
|
| }
|
|
|
| +void TouchSelectionController::SetVelocityStrategyParameters(int halfDecayMs,
|
| + int threshold) {
|
| + if (selection_granularity_strategy_ != GRANULARITY_STRATEGY_VELOCITY)
|
| + return;
|
| +
|
| + VelocityGranularityStrategy* velocityStrategy =
|
| + static_cast<VelocityGranularityStrategy*>(granularity_strategy_.get());
|
| + velocityStrategy->SetStrategyParameters(halfDecayMs, threshold);
|
| +}
|
| +
|
| void TouchSelectionController::OnSelectionBoundsUpdated(
|
| const SelectionBound& start,
|
| const SelectionBound& end) {
|
| if (start == start_ && end_ == end)
|
| return;
|
|
|
| + granularity_strategy_->OnSelectionBoundsUpdated(start, end);
|
| +
|
| start_ = start;
|
| end_ = end;
|
| start_orientation_ = ToTouchHandleOrientation(start_.type());
|
| @@ -235,7 +456,8 @@ void TouchSelectionController::TryActivateSelection() {
|
| }
|
| }
|
|
|
| -void TouchSelectionController::OnHandleDragBegin(const TouchHandle& handle) {
|
| +void TouchSelectionController::OnHandleDragBegin(const TouchHandle& handle,
|
| + base::TimeTicks event_time) {
|
| if (&handle == insertion_handle_.get()) {
|
| client_->OnSelectionEvent(INSERTION_DRAG_STARTED, handle.position());
|
| return;
|
| @@ -245,30 +467,49 @@ void TouchSelectionController::OnHandleDragBegin(const TouchHandle& handle) {
|
| if (&handle == start_selection_handle_.get()) {
|
| base = end_selection_handle_->position() + GetEndLineOffset();
|
| extent = start_selection_handle_->position() + GetStartLineOffset();
|
| + // TODO: HACK
|
| + client_->SelectBetweenCoordinates(base, start_.edge_bottom_rounded() + GetStartLineOffset());
|
| } else {
|
| base = start_selection_handle_->position() + GetStartLineOffset();
|
| extent = end_selection_handle_->position() + GetEndLineOffset();
|
| + // TODO: HACK
|
| + client_->SelectBetweenCoordinates(base, end_.edge_bottom_rounded() + GetEndLineOffset());
|
| }
|
|
|
| + granularity_strategy_->OnHandleDragBegin(handle.position(), event_time, this);
|
| +
|
| // When moving the handle we want to move only the extent point. Before doing
|
| // so we must make sure that the base point is set correctly.
|
| - client_->SelectBetweenCoordinates(base, extent);
|
| + // TODO: HACK - swallow the update
|
| + //client_->SelectBetweenCoordinates(base, extent);
|
| + //OnHandleDragUpdate(handle, handle.position());
|
|
|
| client_->OnSelectionEvent(SELECTION_DRAG_STARTED, handle.position());
|
| }
|
|
|
| void TouchSelectionController::OnHandleDragUpdate(const TouchHandle& handle,
|
| - const gfx::PointF& position) {
|
| + const gfx::PointF& position,
|
| + base::TimeTicks event_time) {
|
| + // TODO: figure out RTL
|
| + // TODO: start/end can switch! Handle!
|
| + bool end_handle_dragged = &handle == end_selection_handle_.get();
|
| // As the position corresponds to the bottom left point of the selection
|
| // bound, offset it by half the corresponding line height.
|
| - gfx::Vector2dF line_offset = &handle == start_selection_handle_.get()
|
| - ? GetStartLineOffset()
|
| - : GetEndLineOffset();
|
| + gfx::Vector2dF line_offset = end_handle_dragged
|
| + ? GetEndLineOffset()
|
| + : GetStartLineOffset();
|
| +
|
| gfx::PointF line_position = position + line_offset;
|
| +
|
| if (&handle == insertion_handle_.get()) {
|
| client_->MoveCaret(line_position);
|
| } else {
|
| - client_->MoveRangeSelectionExtent(line_position, ui::CHARACTER_GRANULARITY);
|
| + TextSelectionGranularity granularity =
|
| + granularity_strategy_->OnHandleDragUpdate(position,
|
| + line_position,
|
| + event_time,
|
| + end_handle_dragged);
|
| + client_->MoveRangeSelectionExtent(line_position, granularity);
|
| }
|
| }
|
|
|
|
|