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

Side by Side 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 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 TrackMouseClicks(event);
26
27 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.
28 return handled;
29
30 if (event.IsOnlyLeftMouseButton()) {
31 delegate_->OnBeforeMouseAction();
32 bool selection_changed = false;
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 (GetRenderText()->IsPointInSelection(event.location())) {
39 delegate_->SetTextBeingDragged(true);
40 } else {
41 MoveCursorTo(event.location(), event.IsShiftDown());
42 selection_changed = true;
43 }
44 break;
45 case 1:
46 // Select the word at the click location on a double click.
47 SelectWordAt(event.location());
48 selection_changed = true;
49 double_click_word_ = GetRenderText()->selection();
50 break;
51 case 2:
52 // Select all the text on a triple click.
53 SelectAll(false);
54 selection_changed = true;
55 break;
56 default:
57 NOTREACHED();
58 }
59 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.
60 }
61
62 // On Linux, middle click should update or paste the selection clipboard.
63 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
64 if (event.IsOnlyMiddleMouseButton()) {
65 delegate_->OnBeforeMouseAction();
66 bool text_changed = false;
67 bool selection_changed = false;
68 if (GetRenderText()->IsPointInSelection(event.location())) {
69 ClearSelection();
70 delegate_->UpdateSelectionClipboard();
71 selection_changed = true;
72 } else if (!delegate_->IsReadOnly()) {
73 delegate_->PasteSelectionClipboard(event);
74 selection_changed = text_changed = true;
75 }
76 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.
77 }
78 #endif
79
80 return true;
81 }
82
83 bool SelectionController::OnMouseDragged(const ui::MouseEvent& event) {
84 // If |drag_selection_timer_| is running, |last_drag_location_| will be used
85 // to update the selection.
86 last_drag_location_ = event.location();
87
88 if (!GetRenderText())
89 return false;
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 if (!GetRenderText())
117 return;
118
119 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.
120 drag_selection_timer_.Stop();
121
122 bool selection_changed = false;
123
124 // Cancel suspected drag initiations, the user was clicking in the selection.
125 if (delegate_->HasTextBeingDragged()) {
126 selection_changed = true;
127 MoveCursorTo(event.location(), false);
128 }
129 delegate_->SetTextBeingDragged(false);
130 delegate_->UpdateSelectionClipboard();
131 delegate_->OnAfterMouseAction(false, selection_changed);
132 }
133
134 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.
135 delegate_->OnBeforeSelectionUpdated();
136 GetRenderText()->SelectAll(reversed);
137 delegate_->OnAfterSelectionUpdated();
138 }
139
140 void SelectionController::SelectWordAt(const gfx::Point& point) {
141 delegate_->OnBeforeSelectionUpdated();
142 gfx::RenderText* render_text = GetRenderText();
143 render_text->MoveCursorTo(point, false);
144 render_text->SelectWord();
145 delegate_->OnAfterSelectionUpdated();
146 }
147
148 void SelectionController::SelectWord() {
149 delegate_->OnBeforeSelectionUpdated();
150 GetRenderText()->SelectWord();
151 delegate_->OnAfterSelectionUpdated();
152 }
153
154 void SelectionController::SelectRange(const gfx::Range& range) {
155 delegate_->OnBeforeSelectionUpdated();
156 GetRenderText()->SelectRange(range);
157 delegate_->OnAfterSelectionUpdated();
158 }
159
160 void SelectionController::MoveCursorTo(const gfx::Point& point, bool select) {
161 delegate_->OnBeforeSelectionUpdated();
162 GetRenderText()->MoveCursorTo(point, select);
163 delegate_->OnAfterSelectionUpdated();
164 }
165
166 void SelectionController::ClearSelection() {
167 delegate_->OnBeforeSelectionUpdated();
168 GetRenderText()->ClearSelection();
169 delegate_->OnAfterSelectionUpdated();
170 }
171
172 void SelectionController::SelectTillEdge(gfx::VisualCursorDirection direction) {
173 delegate_->OnBeforeSelectionUpdated();
174 GetRenderText()->MoveCursor(gfx::LINE_BREAK, direction,
175 gfx::SELECTION_RETAIN);
176 delegate_->OnAfterSelectionUpdated();
177 }
178
179 void SelectionController::TrackMouseClicks(const ui::MouseEvent& event) {
180 if (event.IsOnlyLeftMouseButton()) {
181 base::TimeDelta time_delta = event.time_stamp() - last_click_time_;
182 if (!last_click_time_.is_null() &&
183 time_delta.InMilliseconds() <= GetDoubleClickInterval() &&
184 !View::ExceededDragThreshold(event.location() - last_click_location_)) {
185 // Upon clicking after a triple click, the count should go back to
186 // double click and alternate between double and triple. This assignment
187 // maps 0 to 1, 1 to 2, 2 to 1.
188 aggregated_clicks_ = (aggregated_clicks_ % 2) + 1;
189 } else {
190 aggregated_clicks_ = 0;
191 }
192 last_click_time_ = event.time_stamp();
193 last_click_location_ = event.location();
194 }
195 }
196
197 gfx::RenderText* SelectionController::GetRenderText() {
198 return delegate_->GetRenderTextForSelectionController();
199 }
200
201 void SelectionController::SelectThroughLastDragLocation() {
202 delegate_->OnBeforeMouseAction();
203
204 // TODO(karandeepb): See if this can be handled at the RenderText level.
205 const bool drags_to_end = PlatformStyle::kTextfieldDragVerticallyDragsToEnd;
206 if (drags_to_end && last_drag_location_.y() < 0)
207 SelectTillEdge(gfx::CURSOR_LEFT);
208 else if (drags_to_end && last_drag_location_.y() > delegate_->GetViewHeight())
209 SelectTillEdge(gfx::CURSOR_RIGHT);
210 else
211 MoveCursorTo(last_drag_location_, true);
212
213 if (aggregated_clicks_ == 1) {
214 SelectWord();
215 // Expand the selection so the initially selected word remains selected.
216 gfx::Range selection = GetRenderText()->selection();
217 const size_t min =
218 std::min(selection.GetMin(), double_click_word_.GetMin());
219 const size_t max =
220 std::max(selection.GetMax(), double_click_word_.GetMax());
221 const bool reversed = selection.is_reversed();
222 selection.set_start(reversed ? max : min);
223 selection.set_end(reversed ? min : max);
224 SelectRange(selection);
225 }
226 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.
227 }
228
229 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698