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

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

Issue 2408623002: Views: Extract text selection code from Textfield. (Closed)
Patch Set: 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/style/platform_style.h"
13 #include "ui/views/view.h"
14
15 namespace views {
16
17 SelectionController::SelectionController(Delegate* delegate)
18 : aggregated_clicks_(0), delegate_(delegate) {
19 CHECK(delegate);
20 }
21
22 bool SelectionController::OnMousePressed(const ui::MouseEvent& event,
23 bool handled) {
24 TrackMouseClicks(event);
25
26 if (!GetRenderText() || handled)
27 return handled;
28
29 if (event.IsOnlyLeftMouseButton()) {
30 delegate_->OnBeforeMouseAction();
31 bool selection_changed = false;
32 delegate_->SetTextBeingDragged(false);
33 switch (aggregated_clicks_) {
34 case 0:
35 // If the click location is within existing selection, it may be a
36 // potential drag and drop.
37 if (GetRenderText()->IsPointInSelection(event.location())) {
38 delegate_->SetTextBeingDragged(true);
39 } else {
40 MoveCursorTo(event.location(), event.IsShiftDown());
41 selection_changed = true;
42 }
43 break;
44 case 1:
45 // Select the word at the click location on a double click.
46 SelectWordAt(event.location());
47 selection_changed = true;
48 double_click_word_ = GetRenderText()->selection();
49 break;
50 case 2:
51 // Select all the text on a triple click.
52 SelectAll(false);
53 selection_changed = true;
54 break;
55 default:
56 NOTREACHED();
57 }
58 delegate_->OnAfterMouseAction(false, selection_changed);
59 }
60
61 // On Linux, middle click should update or paste the selection clipboard.
62 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
63 if (event.IsOnlyMiddleMouseButton()) {
64 delegate_->OnBeforeMouseAction();
65 bool text_changed = false;
66 bool selection_changed = false;
67 if (GetRenderText()->IsPointInSelection(event.location())) {
68 ClearSelection();
69 delegate_->UpdateSelectionClipboard();
70 selection_changed = true;
71 } else if (!delegate_->IsReadOnly()) {
72 delegate_->PasteSelectionClipboard(event);
73 selection_changed = text_changed = true;
74 }
75 delegate_->OnAfterMouseAction(text_changed, selection_changed);
76 }
77 #endif
78
79 return true;
80 }
81
82 bool SelectionController::OnMouseDragged(const ui::MouseEvent& event) {
83 // If |drag_selection_timer_| is running, |last_drag_location_| will be used
84 // to update the selection.
85 last_drag_location_ = event.location();
86
87 if (!GetRenderText())
88 return false;
89
90 // Don't adjust the cursor on a potential drag and drop.
91 if (delegate_->HasTextBeingDragged() || !event.IsOnlyLeftMouseButton())
92 return true;
93
94 // A timer is used to continuously scroll while selecting beyond side edges.
95 const int x = event.location().x();
96 if ((x >= 0 && x <= delegate_->GetViewWidth()) ||
97 delegate_->GetDragSelectionDelay() == 0) {
98 drag_selection_timer_.Stop();
99 SelectThroughLastDragLocation();
100 } else if (!drag_selection_timer_.IsRunning()) {
101 // Select through the edge of the visible text, then start the scroll timer.
102 last_drag_location_.set_x(
103 std::min(std::max(0, x), delegate_->GetViewWidth()));
msw 2016/10/14 18:54:20 optional nit: cache delegate_->GetViewWidth()
karandeepb 2016/10/17 07:08:15 Done.
104 SelectThroughLastDragLocation();
105
106 // Todo what happens when this object is destroyed before timer is invoked
msw 2016/10/14 18:54:20 The timer dtor should suffice, right? Remove/Addre
karandeepb 2016/10/17 07:08:15 Done.
107 // but it's scheduled?
108 drag_selection_timer_.Start(
109 FROM_HERE,
110 base::TimeDelta::FromMilliseconds(delegate_->GetDragSelectionDelay()),
111 this, &SelectionController::SelectThroughLastDragLocation);
112 }
113
114 return true;
115 }
116
117 void SelectionController::OnMouseReleased(const ui::MouseEvent& event) {
118 if(!GetRenderText())
119 return;
120
121 delegate_->OnBeforeMouseAction();
122 drag_selection_timer_.Stop();
123
124 bool selection_changed = false;
125
126 // Cancel suspected drag initiations, the user was clicking in the selection.
127 if (delegate_->HasTextBeingDragged()) {
128 selection_changed = true;
129 MoveCursorTo(event.location(), false);
130 }
131 delegate_->SetTextBeingDragged(false);
132 delegate_->UpdateSelectionClipboard();
133 delegate_->OnAfterMouseAction(false, selection_changed);
134 }
135
136 void SelectionController::SelectAll(bool reversed) {
137 delegate_->OnBeforeSelectionUpdated();
138 GetRenderText()->SelectAll(reversed);
139 delegate_->OnAfterSelectionUpdated();
140 }
141
142 void SelectionController::SelectWordAt(const gfx::Point& point) {
143 delegate_->OnBeforeSelectionUpdated();
144 gfx::RenderText* render_text = GetRenderText();
145 render_text->MoveCursorTo(point, false);
146 render_text->SelectWord();
147 delegate_->OnAfterSelectionUpdated();
148 }
149
150 void SelectionController::SelectWord() {
151 delegate_->OnBeforeSelectionUpdated();
152 GetRenderText()->SelectWord();
153 delegate_->OnAfterSelectionUpdated();
154 }
155
156 void SelectionController::SelectRange(const gfx::Range& range) {
157 delegate_->OnBeforeSelectionUpdated();
158 GetRenderText()->SelectRange(range);
159 delegate_->OnAfterSelectionUpdated();
160 }
161
162 void SelectionController::MoveCursorTo(const gfx::Point& point, bool select) {
163 delegate_->OnBeforeSelectionUpdated();
164 GetRenderText()->MoveCursorTo(point, select);
165 delegate_->OnAfterSelectionUpdated();
166 }
167
168 void SelectionController::ClearSelection() {
169 delegate_->OnBeforeSelectionUpdated();
170 GetRenderText()->ClearSelection();
171 delegate_->OnAfterSelectionUpdated();
172 }
173
174 void SelectionController::SelectTillEdge(gfx::VisualCursorDirection direction) {
175 delegate_->OnBeforeSelectionUpdated();
176 GetRenderText()->MoveCursor(gfx::LINE_BREAK, direction,
177 gfx::SELECTION_RETAIN);
178 delegate_->OnAfterSelectionUpdated();
179 }
180
181 void SelectionController::TrackMouseClicks(const ui::MouseEvent& event) {
182 if (event.IsOnlyLeftMouseButton()) {
183 base::TimeDelta time_delta = event.time_stamp() - last_click_time_;
184 if (!last_click_time_.is_null() &&
185 time_delta.InMilliseconds() <= GetDoubleClickInterval() &&
186 !View::ExceededDragThreshold(event.location() - last_click_location_)) {
187 // Upon clicking after a triple click, the count should go back to
188 // double click and alternate between double and triple. This assignment
189 // maps 0 to 1, 1 to 2, 2 to 1.
190 aggregated_clicks_ = (aggregated_clicks_ % 2) + 1;
191 } else {
192 aggregated_clicks_ = 0;
193 }
194 last_click_time_ = event.time_stamp();
195 last_click_location_ = event.location();
196 }
197 }
198
199 gfx::RenderText* SelectionController::GetRenderText() {
200 return delegate_->GetRenderTextForSelection();
201 }
202
203 void SelectionController::SelectThroughLastDragLocation() {
204 delegate_->OnBeforeMouseAction();
205
206 // Todo(karandeepb): See if this can be handled at the RenderText level.
msw 2016/10/14 18:54:20 nit: use TODO, unless this capitalization is also
karandeepb 2016/10/17 07:08:15 Done.
207 const bool drags_to_end = PlatformStyle::kTextfieldDragVerticallyDragsToEnd;
208 if (drags_to_end && last_drag_location_.y() < 0)
209 SelectTillEdge(gfx::CURSOR_LEFT);
210 else if (drags_to_end && last_drag_location_.y() > delegate_->GetViewHeight())
211 SelectTillEdge(gfx::CURSOR_RIGHT);
212 else
213 MoveCursorTo(last_drag_location_, true);
214
215 if (aggregated_clicks_ == 1) {
216 SelectWord();
217 // Expand the selection so the initially selected word remains selected.
218 gfx::Range selection = GetRenderText()->selection();
219 const size_t min =
220 std::min(selection.GetMin(), double_click_word_.GetMin());
221 const size_t max =
222 std::max(selection.GetMax(), double_click_word_.GetMax());
223 const bool reversed = selection.is_reversed();
224 selection.set_start(reversed ? max : min);
225 selection.set_end(reversed ? min : max);
226 SelectRange(selection);
227 }
228 delegate_->OnAfterMouseAction(false, true);
229 }
230
231 } // namespace views
OLDNEW
« ui/views/selection_controller.h ('K') | « ui/views/selection_controller.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698