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

Unified Diff: ui/views/selection_controller.cc

Issue 2408623002: Views: Extract text selection code from Textfield. (Closed)
Patch Set: Rebase. Created 4 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 side-by-side diff with in-line comments
Download patch
Index: ui/views/selection_controller.cc
diff --git a/ui/views/selection_controller.cc b/ui/views/selection_controller.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9a5fe4ef36a831e8dd43ce9c5703d05c58f9016e
--- /dev/null
+++ b/ui/views/selection_controller.cc
@@ -0,0 +1,229 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/selection_controller.h"
+
+#include <algorithm>
+
+#include "ui/events/event.h"
+#include "ui/gfx/render_text.h"
+#include "ui/views/metrics.h"
+#include "ui/views/selection_controller_delegate.h"
+#include "ui/views/style/platform_style.h"
+#include "ui/views/view.h"
+
+namespace views {
+
+SelectionController::SelectionController(SelectionControllerDelegate* delegate)
+ : aggregated_clicks_(0), delegate_(delegate) {
+ DCHECK(delegate);
+}
+
+bool SelectionController::OnMousePressed(const ui::MouseEvent& event,
+ bool handled) {
+ TrackMouseClicks(event);
+
+ if (!GetRenderText() || handled)
msw 2016/10/18 23:15:05 When would there not be a valid RenderText instanc
karandeepb 2016/10/20 04:12:57 Done.
+ return handled;
+
+ if (event.IsOnlyLeftMouseButton()) {
+ delegate_->OnBeforeMouseAction();
+ bool selection_changed = false;
+ delegate_->SetTextBeingDragged(false);
+ switch (aggregated_clicks_) {
+ case 0:
+ // If the click location is within existing selection, it may be a
+ // potential drag and drop.
+ if (GetRenderText()->IsPointInSelection(event.location())) {
+ delegate_->SetTextBeingDragged(true);
+ } else {
+ MoveCursorTo(event.location(), event.IsShiftDown());
+ selection_changed = true;
+ }
+ break;
+ case 1:
+ // Select the word at the click location on a double click.
+ SelectWordAt(event.location());
+ selection_changed = true;
+ double_click_word_ = GetRenderText()->selection();
+ break;
+ case 2:
+ // Select all the text on a triple click.
+ SelectAll(false);
+ selection_changed = true;
+ break;
+ default:
+ NOTREACHED();
+ }
+ delegate_->OnAfterMouseAction(false, selection_changed);
msw 2016/10/18 23:15:05 MoveCursorTo, SelectWordAt, and SelectAll all call
karandeepb 2016/10/20 04:12:57 Done.
+ }
+
+// On Linux, middle click should update or paste the selection clipboard.
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ if (event.IsOnlyMiddleMouseButton()) {
+ delegate_->OnBeforeMouseAction();
+ bool text_changed = false;
+ bool selection_changed = false;
+ if (GetRenderText()->IsPointInSelection(event.location())) {
+ ClearSelection();
+ delegate_->UpdateSelectionClipboard();
+ selection_changed = true;
+ } else if (!delegate_->IsReadOnly()) {
+ delegate_->PasteSelectionClipboard(event);
+ selection_changed = text_changed = true;
+ }
+ delegate_->OnAfterMouseAction(text_changed, selection_changed);
msw 2016/10/18 23:15:05 PasteSelectionClipboard already calls UpdateAfterC
karandeepb 2016/10/20 04:12:57 Done.
+ }
+#endif
+
+ return true;
+}
+
+bool SelectionController::OnMouseDragged(const ui::MouseEvent& event) {
+ // If |drag_selection_timer_| is running, |last_drag_location_| will be used
+ // to update the selection.
+ last_drag_location_ = event.location();
+
+ if (!GetRenderText())
+ return false;
+
+ // Don't adjust the cursor on a potential drag and drop.
+ if (delegate_->HasTextBeingDragged() || !event.IsOnlyLeftMouseButton())
+ return true;
+
+ // A timer is used to continuously scroll while selecting beyond side edges.
+ const int x = event.location().x();
+ const int width = delegate_->GetViewWidth();
+ if ((x >= 0 && x <= width) || delegate_->GetDragSelectionDelay() == 0) {
+ drag_selection_timer_.Stop();
+ SelectThroughLastDragLocation();
+ } else if (!drag_selection_timer_.IsRunning()) {
+ // Select through the edge of the visible text, then start the scroll timer.
+ last_drag_location_.set_x(std::min(std::max(0, x), width));
+ SelectThroughLastDragLocation();
+
+ drag_selection_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(delegate_->GetDragSelectionDelay()),
+ this, &SelectionController::SelectThroughLastDragLocation);
+ }
+
+ return true;
+}
+
+void SelectionController::OnMouseReleased(const ui::MouseEvent& event) {
+ if (!GetRenderText())
+ return;
+
+ delegate_->OnBeforeMouseAction();
msw 2016/10/18 23:15:05 Should these On[Before|After]MouseAction calls onl
karandeepb 2016/10/20 04:12:57 Done.
+ drag_selection_timer_.Stop();
+
+ bool selection_changed = false;
+
+ // Cancel suspected drag initiations, the user was clicking in the selection.
+ if (delegate_->HasTextBeingDragged()) {
+ selection_changed = true;
+ MoveCursorTo(event.location(), false);
+ }
+ delegate_->SetTextBeingDragged(false);
+ delegate_->UpdateSelectionClipboard();
+ delegate_->OnAfterMouseAction(false, selection_changed);
+}
+
+void SelectionController::SelectAll(bool reversed) {
msw 2016/10/18 23:15:05 Most of these helper functions are only called onc
karandeepb 2016/10/20 04:12:57 Done.
+ delegate_->OnBeforeSelectionUpdated();
+ GetRenderText()->SelectAll(reversed);
+ delegate_->OnAfterSelectionUpdated();
+}
+
+void SelectionController::SelectWordAt(const gfx::Point& point) {
+ delegate_->OnBeforeSelectionUpdated();
+ gfx::RenderText* render_text = GetRenderText();
+ render_text->MoveCursorTo(point, false);
+ render_text->SelectWord();
+ delegate_->OnAfterSelectionUpdated();
+}
+
+void SelectionController::SelectWord() {
+ delegate_->OnBeforeSelectionUpdated();
+ GetRenderText()->SelectWord();
+ delegate_->OnAfterSelectionUpdated();
+}
+
+void SelectionController::SelectRange(const gfx::Range& range) {
+ delegate_->OnBeforeSelectionUpdated();
+ GetRenderText()->SelectRange(range);
+ delegate_->OnAfterSelectionUpdated();
+}
+
+void SelectionController::MoveCursorTo(const gfx::Point& point, bool select) {
+ delegate_->OnBeforeSelectionUpdated();
+ GetRenderText()->MoveCursorTo(point, select);
+ delegate_->OnAfterSelectionUpdated();
+}
+
+void SelectionController::ClearSelection() {
+ delegate_->OnBeforeSelectionUpdated();
+ GetRenderText()->ClearSelection();
+ delegate_->OnAfterSelectionUpdated();
+}
+
+void SelectionController::SelectTillEdge(gfx::VisualCursorDirection direction) {
+ delegate_->OnBeforeSelectionUpdated();
+ GetRenderText()->MoveCursor(gfx::LINE_BREAK, direction,
+ gfx::SELECTION_RETAIN);
+ delegate_->OnAfterSelectionUpdated();
+}
+
+void SelectionController::TrackMouseClicks(const ui::MouseEvent& event) {
+ if (event.IsOnlyLeftMouseButton()) {
+ base::TimeDelta time_delta = event.time_stamp() - last_click_time_;
+ if (!last_click_time_.is_null() &&
+ time_delta.InMilliseconds() <= GetDoubleClickInterval() &&
+ !View::ExceededDragThreshold(event.location() - last_click_location_)) {
+ // Upon clicking after a triple click, the count should go back to
+ // double click and alternate between double and triple. This assignment
+ // maps 0 to 1, 1 to 2, 2 to 1.
+ aggregated_clicks_ = (aggregated_clicks_ % 2) + 1;
+ } else {
+ aggregated_clicks_ = 0;
+ }
+ last_click_time_ = event.time_stamp();
+ last_click_location_ = event.location();
+ }
+}
+
+gfx::RenderText* SelectionController::GetRenderText() {
+ return delegate_->GetRenderTextForSelectionController();
+}
+
+void SelectionController::SelectThroughLastDragLocation() {
+ delegate_->OnBeforeMouseAction();
+
+ // TODO(karandeepb): See if this can be handled at the RenderText level.
+ const bool drags_to_end = PlatformStyle::kTextfieldDragVerticallyDragsToEnd;
+ if (drags_to_end && last_drag_location_.y() < 0)
+ SelectTillEdge(gfx::CURSOR_LEFT);
+ else if (drags_to_end && last_drag_location_.y() > delegate_->GetViewHeight())
+ SelectTillEdge(gfx::CURSOR_RIGHT);
+ else
+ MoveCursorTo(last_drag_location_, true);
+
+ if (aggregated_clicks_ == 1) {
+ SelectWord();
+ // Expand the selection so the initially selected word remains selected.
+ gfx::Range selection = GetRenderText()->selection();
+ const size_t min =
+ std::min(selection.GetMin(), double_click_word_.GetMin());
+ const size_t max =
+ std::max(selection.GetMax(), double_click_word_.GetMax());
+ const bool reversed = selection.is_reversed();
+ selection.set_start(reversed ? max : min);
+ selection.set_end(reversed ? min : max);
+ SelectRange(selection);
+ }
+ delegate_->OnAfterMouseAction(false, true);
msw 2016/10/18 23:15:05 SelectTillEdge, MoveCursorTo, SelectWord, and Sele
karandeepb 2016/10/20 04:12:57 Done.
msw 2016/10/21 02:14:15 Acknowledged; you're right.
+}
+
+} // namespace views

Powered by Google App Engine
This is Rietveld 408576698