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

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/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 an 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();
msw 2016/10/21 02:14:16 optional nit: you may wrap the whole switch in On[
karandeepb 2016/10/21 05:04:32 Will keep it as it is. Some of these functions don
42 const bool selection_changed =
43 render_text->MoveCursorTo(event.location(), event.IsShiftDown());
44 delegate_->OnAfterMouseAction(false, selection_changed);
45 }
46 break;
47 case 1:
48 // Select the word at the click location on a double click.
49 delegate_->OnBeforeMouseAction();
50 render_text->MoveCursorTo(event.location(), false);
51 render_text->SelectWord();
52 delegate_->OnAfterMouseAction(false, true);
53 double_click_word_ = render_text->selection();
54 break;
55 case 2:
56 // Select all the text on a triple click.
57 delegate_->OnBeforeMouseAction();
58 render_text->SelectAll(false);
59 delegate_->OnAfterMouseAction(false, true);
60 break;
61 default:
62 NOTREACHED();
63 }
64 }
65
66 // On Linux, middle click should update or paste the selection clipboard.
67 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
68 if (event.IsOnlyMiddleMouseButton()) {
69 if (render_text->IsPointInSelection(event.location())) {
70 delegate_->OnBeforeMouseAction();
71 render_text->ClearSelection();
72 delegate_->UpdateSelectionClipboard();
73 delegate_->OnAfterMouseAction(false, true);
74 } else if (!delegate_->IsReadOnly()) {
75 delegate_->OnBeforeMouseAction();
76 const bool selection_changed =
77 render_text->MoveCursorTo(event.location(), false);
78 const bool text_changed = delegate_->PasteSelectionClipboard();
79 delegate_->OnAfterMouseAction(text_changed,
80 selection_changed | text_changed);
81 }
82 }
83 #endif
84
85 return true;
86 }
87
88 bool SelectionController::OnMouseDragged(const ui::MouseEvent& event) {
89 DCHECK(GetRenderText());
90 // If |drag_selection_timer_| is running, |last_drag_location_| will be used
91 // to update the selection.
92 last_drag_location_ = event.location();
93
94 // Don't adjust the cursor on a potential drag and drop.
95 if (delegate_->HasTextBeingDragged() || !event.IsOnlyLeftMouseButton())
96 return true;
97
98 // A timer is used to continuously scroll while selecting beyond side edges.
99 const int x = event.location().x();
100 const int width = delegate_->GetViewWidth();
101 const int drag_selection_delay = delegate_->GetDragSelectionDelay();
102 if ((x >= 0 && x <= width) || drag_selection_delay == 0) {
103 drag_selection_timer_.Stop();
104 SelectThroughLastDragLocation();
105 } else if (!drag_selection_timer_.IsRunning()) {
106 // Select through the edge of the visible text, then start the scroll timer.
107 last_drag_location_.set_x(std::min(std::max(0, x), width));
108 SelectThroughLastDragLocation();
109
110 drag_selection_timer_.Start(
111 FROM_HERE, base::TimeDelta::FromMilliseconds(drag_selection_delay),
112 this, &SelectionController::SelectThroughLastDragLocation);
113 }
114
115 return true;
116 }
117
118 void SelectionController::OnMouseReleased(const ui::MouseEvent& event) {
119 gfx::RenderText* render_text = GetRenderText();
120 DCHECK(render_text);
121
122 drag_selection_timer_.Stop();
123
124 // Cancel suspected drag initiations, the user was clicking in the selection.
125 if (delegate_->HasTextBeingDragged()) {
126 delegate_->OnBeforeMouseAction();
127 const bool selection_changed =
128 render_text->MoveCursorTo(event.location(), false);
129 delegate_->OnAfterMouseAction(false, selection_changed);
130 }
131 delegate_->SetTextBeingDragged(false);
132
133 if (!render_text->selection().is_empty())
134 delegate_->UpdateSelectionClipboard();
135 }
136
137 void SelectionController::TrackMouseClicks(const ui::MouseEvent& event) {
138 if (event.IsOnlyLeftMouseButton()) {
139 base::TimeDelta time_delta = event.time_stamp() - last_click_time_;
140 if (!last_click_time_.is_null() &&
141 time_delta.InMilliseconds() <= GetDoubleClickInterval() &&
142 !View::ExceededDragThreshold(event.location() - last_click_location_)) {
143 // Upon clicking after a triple click, the count should go back to
144 // double click and alternate between double and triple. This assignment
145 // maps 0 to 1, 1 to 2, 2 to 1.
146 aggregated_clicks_ = (aggregated_clicks_ % 2) + 1;
147 } else {
148 aggregated_clicks_ = 0;
149 }
150 last_click_time_ = event.time_stamp();
151 last_click_location_ = event.location();
152 }
153 }
154
155 gfx::RenderText* SelectionController::GetRenderText() {
156 return delegate_->GetRenderTextForSelectionController();
157 }
158
159 void SelectionController::SelectThroughLastDragLocation() {
160 gfx::RenderText* render_text = GetRenderText();
161 DCHECK(render_text);
162
163 delegate_->OnBeforeMouseAction();
164
165 // TODO(karandeepb): See if this can be handled at the RenderText level.
166 const bool drags_to_end = PlatformStyle::kTextfieldDragVerticallyDragsToEnd;
167 if (drags_to_end && last_drag_location_.y() < 0) {
168 render_text->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT,
169 gfx::SELECTION_RETAIN);
170 } else if (drags_to_end &&
171 last_drag_location_.y() > delegate_->GetViewHeight()) {
172 render_text->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT,
173 gfx::SELECTION_RETAIN);
174 } else {
175 render_text->MoveCursorTo(last_drag_location_, true);
176 }
177
178 if (aggregated_clicks_ == 1) {
179 render_text->SelectWord();
180 // Expand the selection so the initially selected word remains selected.
181 gfx::Range selection = render_text->selection();
182 const size_t min =
183 std::min(selection.GetMin(), double_click_word_.GetMin());
184 const size_t max =
185 std::max(selection.GetMax(), double_click_word_.GetMax());
186 const bool reversed = selection.is_reversed();
187 selection.set_start(reversed ? max : min);
188 selection.set_end(reversed ? min : max);
189 render_text->SelectRange(selection);
190 }
191 delegate_->OnAfterMouseAction(false, true);
192 }
193
194 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698