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

Side by Side Diff: ui/views/selection_controller.cc

Issue 2408623002: Views: Extract text selection code from Textfield. (Closed)
Patch Set: Changes to PasteSelectionClipboard 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/views/selection_controller.h"
6
7 #include <algorithm>
8
9 #include "ui/events/event.h"
10 #include "ui/gfx/render_text.h"
11 #include "ui/views/metrics.h"
12 #include "ui/views/selection_controller_delegate.h"
13 #include "ui/views/style/platform_style.h"
14 #include "ui/views/view.h"
15
16 namespace views {
17
18 SelectionController::SelectionController(SelectionControllerDelegate* delegate)
19 : aggregated_clicks_(0), delegate_(delegate) {
20 DCHECK(delegate);
21 }
22
23 bool SelectionController::OnMousePressed(const ui::MouseEvent& event,
24 bool handled) {
25 gfx::RenderText* render_text = GetRenderText();
26 DCHECK(render_text);
27
28 TrackMouseClicks(event);
29 if (handled)
30 return true;
31
32 if (event.IsOnlyLeftMouseButton()) {
33 delegate_->SetTextBeingDragged(false);
34 switch (aggregated_clicks_) {
35 case 0:
36 // If the click location is within existing selection, it may be a
37 // potential drag and drop.
38 if (render_text->IsPointInSelection(event.location())) {
39 delegate_->SetTextBeingDragged(true);
40 } else {
41 delegate_->OnBeforeMouseAction();
42 render_text->MoveCursorTo(event.location(), event.IsShiftDown());
43 delegate_->OnAfterMouseAction(false, true);
44 }
45 break;
46 case 1:
47 // Select the word at the click location on a double click.
48 delegate_->OnBeforeMouseAction();
49 render_text->MoveCursorTo(event.location(), false);
50 render_text->SelectWord();
51 delegate_->OnAfterMouseAction(false, true);
52 double_click_word_ = render_text->selection();
53 break;
54 case 2:
55 // Select all the text on a triple click.
56 delegate_->OnBeforeMouseAction();
57 render_text->SelectAll(false);
58 delegate_->OnAfterMouseAction(false, true);
59 break;
60 default:
61 NOTREACHED();
62 }
63 }
64
65 // On Linux, middle click should update or paste the selection clipboard.
66 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
67 if (event.IsOnlyMiddleMouseButton()) {
68 if (render_text->IsPointInSelection(event.location())) {
69 delegate_->OnBeforeMouseAction();
70 render_text->ClearSelection();
71 delegate_->OnAfterMouseAction(false, true);
72 delegate_->UpdateSelectionClipboard();
73 } else if (!delegate_->IsReadOnly()) {
74 delegate_->OnBeforeMouseAction();
75 render_text->MoveCursorTo(event.location(), false);
76 const bool text_changed = delegate_->PasteSelectionClipboard();
77 delegate_->OnAfterMouseAction(text_changed, true);
78 }
79 }
80 #endif
81
82 return true;
83 }
84
85 bool SelectionController::OnMouseDragged(const ui::MouseEvent& event) {
86 DCHECK(GetRenderText());
87 // If |drag_selection_timer_| is running, |last_drag_location_| will be used
88 // to update the selection.
89 last_drag_location_ = event.location();
90
91 // Don't adjust the cursor on a potential drag and drop.
92 if (delegate_->HasTextBeingDragged() || !event.IsOnlyLeftMouseButton())
93 return true;
94
95 // A timer is used to continuously scroll while selecting beyond side edges.
96 const int x = event.location().x();
97 const int width = delegate_->GetViewWidth();
98 if ((x >= 0 && x <= width) || delegate_->GetDragSelectionDelay() == 0) {
99 drag_selection_timer_.Stop();
100 SelectThroughLastDragLocation();
101 } else if (!drag_selection_timer_.IsRunning()) {
102 // Select through the edge of the visible text, then start the scroll timer.
103 last_drag_location_.set_x(std::min(std::max(0, x), width));
104 SelectThroughLastDragLocation();
105
106 drag_selection_timer_.Start(
107 FROM_HERE,
108 base::TimeDelta::FromMilliseconds(delegate_->GetDragSelectionDelay()),
109 this, &SelectionController::SelectThroughLastDragLocation);
110 }
111
112 return true;
113 }
114
115 void SelectionController::OnMouseReleased(const ui::MouseEvent& event) {
116 gfx::RenderText* render_text = GetRenderText();
117 DCHECK(render_text);
118
119 drag_selection_timer_.Stop();
120
121 // Cancel suspected drag initiations, the user was clicking in the selection.
122 if (delegate_->HasTextBeingDragged()) {
123 delegate_->OnBeforeMouseAction();
124 render_text->MoveCursorTo(event.location(), false);
125 delegate_->OnAfterMouseAction(false, true);
126 }
127 delegate_->SetTextBeingDragged(false);
128
129 if (!render_text->selection().is_empty())
130 delegate_->UpdateSelectionClipboard();
131 }
132
133 void SelectionController::TrackMouseClicks(const ui::MouseEvent& event) {
134 if (event.IsOnlyLeftMouseButton()) {
135 base::TimeDelta time_delta = event.time_stamp() - last_click_time_;
136 if (!last_click_time_.is_null() &&
137 time_delta.InMilliseconds() <= GetDoubleClickInterval() &&
138 !View::ExceededDragThreshold(event.location() - last_click_location_)) {
139 // Upon clicking after a triple click, the count should go back to
140 // double click and alternate between double and triple. This assignment
141 // maps 0 to 1, 1 to 2, 2 to 1.
142 aggregated_clicks_ = (aggregated_clicks_ % 2) + 1;
143 } else {
144 aggregated_clicks_ = 0;
145 }
146 last_click_time_ = event.time_stamp();
147 last_click_location_ = event.location();
148 }
149 }
150
151 gfx::RenderText* SelectionController::GetRenderText() {
152 return delegate_->GetRenderTextForSelectionController();
153 }
154
155 void SelectionController::SelectThroughLastDragLocation() {
156 gfx::RenderText* render_text = GetRenderText();
157 DCHECK(render_text);
158
159 delegate_->OnBeforeMouseAction();
160
161 // TODO(karandeepb): See if this can be handled at the RenderText level.
162 const bool drags_to_end = PlatformStyle::kTextfieldDragVerticallyDragsToEnd;
163 if (drags_to_end && last_drag_location_.y() < 0) {
164 render_text->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT,
165 gfx::SELECTION_RETAIN);
166 } else if (drags_to_end &&
167 last_drag_location_.y() > delegate_->GetViewHeight()) {
168 render_text->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT,
169 gfx::SELECTION_RETAIN);
170 } else {
171 render_text->MoveCursorTo(last_drag_location_, true);
172 }
173
174 if (aggregated_clicks_ == 1) {
175 render_text->SelectWord();
176 // Expand the selection so the initially selected word remains selected.
177 gfx::Range selection = render_text->selection();
178 const size_t min =
179 std::min(selection.GetMin(), double_click_word_.GetMin());
180 const size_t max =
181 std::max(selection.GetMax(), double_click_word_.GetMax());
182 const bool reversed = selection.is_reversed();
183 selection.set_start(reversed ? max : min);
184 selection.set_end(reversed ? min : max);
185 render_text->SelectRange(selection);
186 }
187 delegate_->OnAfterMouseAction(false, true);
188 }
189
190 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698