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

Side by Side Diff: views/touchui/touch_selection_controller_impl.cc

Issue 7740024: Implement touch selection menu. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: changed SetBounds to Layout Created 9 years, 3 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 | Annotate | Revision Log
« no previous file with comments | « views/touchui/touch_selection_controller_impl.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "views/touchui/touch_selection_controller_impl.h" 5 #include "views/touchui/touch_selection_controller_impl.h"
6 6
7 #include "base/time.h"
8 #include "grit/ui_strings.h"
9 #include "ui/base/l10n/l10n_util.h"
10 #include "ui/base/resource/resource_bundle.h"
7 #include "ui/gfx/canvas.h" 11 #include "ui/gfx/canvas.h"
8 #include "ui/gfx/canvas_skia.h" 12 #include "ui/gfx/canvas_skia.h"
9 #include "ui/gfx/path.h" 13 #include "ui/gfx/path.h"
10 #include "ui/gfx/rect.h" 14 #include "ui/gfx/rect.h"
15 #include "ui/gfx/screen.h"
11 #include "ui/gfx/size.h" 16 #include "ui/gfx/size.h"
12 #include "ui/gfx/transform.h" 17 #include "ui/gfx/transform.h"
18 #include "views/background.h"
19 #include "views/controls/button/button.h"
20 #include "views/controls/button/text_button.h"
21 #include "views/controls/label.h"
22 #include "views/layout/box_layout.h"
13 #include "views/widget/widget.h" 23 #include "views/widget/widget.h"
14 #include "views/controls/label.h"
15 #include "views/background.h"
16 24
17 namespace { 25 namespace {
18 26
19 // Constants defining the visual attributes of selection handles 27 // Constants defining the visual attributes of selection handles
20 const int kSelectionHandleRadius = 10; 28 const int kSelectionHandleRadius = 10;
21 const int kSelectionHandleCursorHeight = 10; 29 const int kSelectionHandleCursorHeight = 10;
22 const int kSelectionHandleAlpha = 0x7F; 30 const int kSelectionHandleAlpha = 0x7F;
23 const SkColor kSelectionHandleColor = 31 const SkColor kSelectionHandleColor =
24 SkColorSetA(SK_ColorBLUE, kSelectionHandleAlpha); 32 SkColorSetA(SK_ColorBLUE, kSelectionHandleAlpha);
25 33
34 const int kContextMenuCommands[] = {IDS_APP_CUT,
35 IDS_APP_COPY,
36 // TODO(varunjain): PASTE is acting funny due to some gtk clipboard issue.
37 // Uncomment the following when that is fixed.
38 // IDS_APP_PASTE,
39 IDS_APP_DELETE,
40 IDS_APP_SELECT_ALL};
41 const int kContextMenuPadding = 2;
42 const int kContextMenuTimoutMs = 1000;
43 const int kContextMenuVerticalOffset = 10;
44
26 // Convenience struct to represent a circle shape. 45 // Convenience struct to represent a circle shape.
27 struct Circle { 46 struct Circle {
28 int radius; 47 int radius;
29 gfx::Point center; 48 gfx::Point center;
30 SkColor color; 49 SkColor color;
31 }; 50 };
32 51
33 // Creates a widget to host SelectionHandleView. 52 // Creates a widget to host SelectionHandleView.
34 views::Widget* CreateSelectionHandleWidget() { 53 views::Widget* CreateTouchSelectionPopupWidget() {
35 views::Widget* widget = new views::Widget; 54 views::Widget* widget = new views::Widget;
36 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); 55 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
37 params.can_activate = false; 56 params.can_activate = false;
38 params.transparent = true; 57 params.transparent = true;
39 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 58 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
40 widget->Init(params); 59 widget->Init(params);
41 return widget; 60 return widget;
42 } 61 }
43 62
44 void PaintCircle(const Circle& circle, gfx::Canvas* canvas) { 63 void PaintCircle(const Circle& circle, gfx::Canvas* canvas) {
(...skipping 16 matching lines...) Expand all
61 80
62 } // namespace 81 } // namespace
63 82
64 namespace views { 83 namespace views {
65 84
66 // A View that displays the text selection handle. 85 // A View that displays the text selection handle.
67 class TouchSelectionControllerImpl::SelectionHandleView : public View { 86 class TouchSelectionControllerImpl::SelectionHandleView : public View {
68 public: 87 public:
69 SelectionHandleView(TouchSelectionControllerImpl* controller) 88 SelectionHandleView(TouchSelectionControllerImpl* controller)
70 : controller_(controller) { 89 : controller_(controller) {
71 widget_.reset(CreateSelectionHandleWidget()); 90 widget_.reset(CreateTouchSelectionPopupWidget());
72 widget_->SetContentsView(this); 91 widget_->SetContentsView(this);
73 widget_->SetAlwaysOnTop(true); 92 widget_->SetAlwaysOnTop(true);
74 93
75 // We are owned by the TouchSelectionController. 94 // We are owned by the TouchSelectionController.
76 set_parent_owned(false); 95 set_parent_owned(false);
77 } 96 }
78 97
79 virtual ~SelectionHandleView() { 98 virtual ~SelectionHandleView() {
80 widget_->Close(); 99 widget_->Close();
81 } 100 }
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
134 return widget_->GetClientAreaScreenBounds().origin(); 153 return widget_->GetClientAreaScreenBounds().origin();
135 } 154 }
136 155
137 private: 156 private:
138 scoped_ptr<Widget> widget_; 157 scoped_ptr<Widget> widget_;
139 TouchSelectionControllerImpl* controller_; 158 TouchSelectionControllerImpl* controller_;
140 159
141 DISALLOW_COPY_AND_ASSIGN(SelectionHandleView); 160 DISALLOW_COPY_AND_ASSIGN(SelectionHandleView);
142 }; 161 };
143 162
163 // A View that displays the touch context menu.
164 class TouchSelectionControllerImpl::TouchContextMenuView
165 : public ButtonListener,
166 public View {
167 public:
168 TouchContextMenuView(TouchSelectionControllerImpl* controller)
169 : controller_(controller) {
170 widget_.reset(CreateTouchSelectionPopupWidget());
171 widget_->SetContentsView(this);
172 widget_->SetAlwaysOnTop(true);
173
174 // We are owned by the TouchSelectionController.
175 set_parent_owned(false);
176 SetLayoutManager(new BoxLayout(BoxLayout::kHorizontal, kContextMenuPadding,
177 kContextMenuPadding, kContextMenuPadding));
178 }
179
180 virtual ~TouchContextMenuView() {
181 widget_->Close();
182 }
183
184 virtual void SetVisible(bool visible) OVERRIDE {
185 // We simply show/hide the container widget.
186 if (visible != widget_->IsVisible()) {
187 if (visible)
188 widget_->Show();
189 else
190 widget_->Hide();
191 }
192 View::SetVisible(visible);
193 }
194
195 void SetScreenPosition(const gfx::Point& position) {
196 RefreshButtonsAndSetWidgetPosition(position);
197 }
198
199 gfx::Point GetScreenPosition() {
200 return widget_->GetClientAreaScreenBounds().origin();
201 }
202
203 // ButtonListener
204 virtual void ButtonPressed(Button* sender, const views::Event& event) {
205 controller_->ExecuteCommand(sender->tag());
206 }
207
208 private:
209 // Queries the client view for what elements to show in the menu and sizes
210 // the menu appropriately.
211 void RefreshButtonsAndSetWidgetPosition(const gfx::Point& position) {
212 RemoveAllChildViews(true);
213 int total_width = 0;
214 int height = 0;
215 for (size_t i = 0; i < arraysize(kContextMenuCommands); i++) {
216 int command_id = kContextMenuCommands[i];
217 if (controller_->IsCommandIdEnabled(command_id)) {
218 TextButton* button = new TextButton(this,
219 UTF16ToWide(l10n_util::GetStringUTF16(command_id)));
220 button->set_focusable(true);
221 button->set_request_focus_on_press(false);
222 button->set_prefix_type(TextButton::PREFIX_HIDE);
223 button->SetEnabledColor(SK_ColorWHITE);
224 button->SetHoverColor(SK_ColorWHITE);
225 button->set_background(
226 Background::CreateSolidBackground(SK_ColorBLACK));
227 button->set_alignment(TextButton::ALIGN_CENTER);
228 button->SetFont(ui::ResourceBundle::GetSharedInstance().GetFont(
229 ui::ResourceBundle::LargeFont));
230 button->set_tag(command_id);
231 AddChildView(button);
232 gfx::Size button_size = button->GetPreferredSize();
233 total_width += button_size.width() + kContextMenuPadding;
234 if (height < button_size.height())
235 height = button_size.height();
236 }
237 }
238 gfx::Rect widget_bounds(position.x() - total_width / 2,
239 position.y() - height,
240 total_width,
241 height);
242 gfx::Rect monitor_bounds =
243 gfx::Screen::GetMonitorAreaNearestPoint(position);
244 widget_->SetBounds(widget_bounds.AdjustToFit(monitor_bounds));
245 Layout();
246 }
247
248 scoped_ptr<Widget> widget_;
249 TouchSelectionControllerImpl* controller_;
250
251 DISALLOW_COPY_AND_ASSIGN(TouchContextMenuView);
252 };
253
144 TouchSelectionControllerImpl::TouchSelectionControllerImpl( 254 TouchSelectionControllerImpl::TouchSelectionControllerImpl(
145 TouchSelectionClientView* client_view) 255 TouchSelectionClientView* client_view)
146 : client_view_(client_view), 256 : client_view_(client_view),
147 selection_handle_1_(new SelectionHandleView(this)), 257 selection_handle_1_(new SelectionHandleView(this)),
148 selection_handle_2_(new SelectionHandleView(this)), 258 selection_handle_2_(new SelectionHandleView(this)),
259 context_menu_(new TouchContextMenuView(this)),
149 dragging_handle_(NULL) { 260 dragging_handle_(NULL) {
150 } 261 }
151 262
152 TouchSelectionControllerImpl::~TouchSelectionControllerImpl() { 263 TouchSelectionControllerImpl::~TouchSelectionControllerImpl() {
153 } 264 }
154 265
155 void TouchSelectionControllerImpl::SelectionChanged(const gfx::Point& p1, 266 void TouchSelectionControllerImpl::SelectionChanged(const gfx::Point& p1,
156 const gfx::Point& p2) { 267 const gfx::Point& p2) {
157 gfx::Point screen_pos_1(p1); 268 gfx::Point screen_pos_1(p1);
158 View::ConvertPointToScreen(client_view_, &screen_pos_1); 269 View::ConvertPointToScreen(client_view_, &screen_pos_1);
159 gfx::Point screen_pos_2(p2); 270 gfx::Point screen_pos_2(p2);
160 View::ConvertPointToScreen(client_view_, &screen_pos_2); 271 View::ConvertPointToScreen(client_view_, &screen_pos_2);
161 272
162 if (dragging_handle_) { 273 if (dragging_handle_) {
163 // We need to reposition only the selection handle that is being dragged. 274 // We need to reposition only the selection handle that is being dragged.
164 // The other handle stays the same. Also, the selection handle being dragged 275 // The other handle stays the same. Also, the selection handle being dragged
165 // will always be at the end of selection, while the other handle will be at 276 // will always be at the end of selection, while the other handle will be at
166 // the start. 277 // the start.
167 dragging_handle_->SetScreenPosition(screen_pos_2); 278 dragging_handle_->SetScreenPosition(screen_pos_2);
168 } else { 279 } else {
280 UpdateContextMenu(p1, p2);
281
169 // Check if there is any selection at all. 282 // Check if there is any selection at all.
170 if (screen_pos_1 == screen_pos_2) { 283 if (screen_pos_1 == screen_pos_2) {
171 selection_handle_1_->SetVisible(false); 284 selection_handle_1_->SetVisible(false);
172 selection_handle_2_->SetVisible(false); 285 selection_handle_2_->SetVisible(false);
173 return; 286 return;
174 } 287 }
175 288
176 if (client_view_->bounds().Contains(p1)) { 289 if (client_view_->bounds().Contains(p1)) {
177 selection_handle_1_->SetScreenPosition(screen_pos_1); 290 selection_handle_1_->SetScreenPosition(screen_pos_1);
178 selection_handle_1_->SetVisible(true); 291 selection_handle_1_->SetVisible(true);
179 } else { 292 } else {
180 selection_handle_1_->SetVisible(false); 293 selection_handle_1_->SetVisible(false);
181 } 294 }
182 295
183 if (client_view_->bounds().Contains(p2)) { 296 if (client_view_->bounds().Contains(p2)) {
184 selection_handle_2_->SetScreenPosition(screen_pos_2); 297 selection_handle_2_->SetScreenPosition(screen_pos_2);
185 selection_handle_2_->SetVisible(true); 298 selection_handle_2_->SetVisible(true);
186 } else { 299 } else {
187 selection_handle_2_->SetVisible(false); 300 selection_handle_2_->SetVisible(false);
188 } 301 }
189 } 302 }
190 } 303 }
191 304
192 void TouchSelectionControllerImpl::ClientViewLostFocus() { 305 void TouchSelectionControllerImpl::ClientViewLostFocus() {
193 selection_handle_1_->SetVisible(false); 306 selection_handle_1_->SetVisible(false);
194 selection_handle_2_->SetVisible(false); 307 selection_handle_2_->SetVisible(false);
308 HideContextMenu();
195 } 309 }
196 310
197 void TouchSelectionControllerImpl::SelectionHandleDragged( 311 void TouchSelectionControllerImpl::SelectionHandleDragged(
198 const gfx::Point& drag_pos) { 312 const gfx::Point& drag_pos) {
313 // We do not want to show the context menu while dragging.
314 HideContextMenu();
315 context_menu_timer_.Start(
316 base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs),
317 this,
318 &TouchSelectionControllerImpl::ContextMenuTimerFired);
319
199 if (client_view_->GetWidget()) { 320 if (client_view_->GetWidget()) {
200 DCHECK(dragging_handle_); 321 DCHECK(dragging_handle_);
201 // Find the stationary selection handle. 322 // Find the stationary selection handle.
202 SelectionHandleView* fixed_handle = selection_handle_1_.get(); 323 SelectionHandleView* fixed_handle = selection_handle_1_.get();
203 if (fixed_handle == dragging_handle_) 324 if (fixed_handle == dragging_handle_)
204 fixed_handle = selection_handle_2_.get(); 325 fixed_handle = selection_handle_2_.get();
205 326
206 // Find selection end points in client_view's coordinate system. 327 // Find selection end points in client_view's coordinate system.
207 gfx::Point p1(drag_pos.x() + kSelectionHandleRadius, drag_pos.y()); 328 gfx::Point p1(drag_pos.x() + kSelectionHandleRadius, drag_pos.y());
208 ConvertPointToClientView(dragging_handle_, &p1); 329 ConvertPointToClientView(dragging_handle_, &p1);
209 330
210 gfx::Point p2(kSelectionHandleRadius, 0); 331 gfx::Point p2(kSelectionHandleRadius, 0);
211 ConvertPointToClientView(fixed_handle, &p2); 332 ConvertPointToClientView(fixed_handle, &p2);
212 333
213 // Instruct client_view to select the region between p1 and p2. The position 334 // Instruct client_view to select the region between p1 and p2. The position
214 // of |fixed_handle| is the start and that of |dragging_handle| is the end 335 // of |fixed_handle| is the start and that of |dragging_handle| is the end
215 // of selection. 336 // of selection.
216 client_view_->SelectRect(p2, p1); 337 client_view_->SelectRect(p2, p1);
217 } 338 }
218 } 339 }
219 340
220 void TouchSelectionControllerImpl::ConvertPointToClientView( 341 void TouchSelectionControllerImpl::ConvertPointToClientView(
221 SelectionHandleView* source, gfx::Point* point) { 342 SelectionHandleView* source, gfx::Point* point) {
222 View::ConvertPointToScreen(source, point); 343 View::ConvertPointToScreen(source, point);
223 gfx::Rect r = client_view_->GetWidget()->GetClientAreaScreenBounds(); 344 gfx::Rect r = client_view_->GetWidget()->GetClientAreaScreenBounds();
224 point->SetPoint(point->x() - r.x(), point->y() - r.y()); 345 point->SetPoint(point->x() - r.x(), point->y() - r.y());
225 View::ConvertPointFromWidget(client_view_, point); 346 View::ConvertPointFromWidget(client_view_, point);
226 } 347 }
227 348
349 bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const {
350 return client_view_->IsCommandIdEnabled(command_id);
351 }
352
353 void TouchSelectionControllerImpl::ExecuteCommand(int command_id) {
354 HideContextMenu();
355 client_view_->ExecuteCommand(command_id);
356 }
357
358 void TouchSelectionControllerImpl::ContextMenuTimerFired() {
359 // Get selection end points in client_view's space.
360 gfx::Point p1(kSelectionHandleRadius, 0);
361 ConvertPointToClientView(selection_handle_1_.get(), &p1);
362 gfx::Point p2(kSelectionHandleRadius, 0);
363 ConvertPointToClientView(selection_handle_2_.get(), &p2);
364
365 // if selection is completely inside the view, we display the context menu
366 // in the middle of the end points on the top. Else, we show the menu on the
367 // top border of the view in the center.
368 gfx::Point menu_pos;
369 if (client_view_->bounds().Contains(p1) &&
370 client_view_->bounds().Contains(p2)) {
371 menu_pos.set_x((p1.x() + p2.x()) / 2);
372 menu_pos.set_y(std::min(p1.y(), p2.y()) - kContextMenuVerticalOffset);
373 } else {
374 menu_pos.set_x(client_view_->x() + client_view_->width() / 2);
375 menu_pos.set_y(client_view_->y());
376 }
377
378 View::ConvertPointToScreen(client_view_, &menu_pos);
379
380 context_menu_->SetScreenPosition(menu_pos);
381 context_menu_->SetVisible(true);
382 }
383
384 void TouchSelectionControllerImpl::UpdateContextMenu(const gfx::Point& p1,
385 const gfx::Point& p2) {
386 // Hide context menu to be shown when the timer fires.
387 HideContextMenu();
388
389 // If there is selection, we restart the context menu timer.
390 if (p1 != p2) {
391 context_menu_timer_.Start(
392 base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs),
393 this,
394 &TouchSelectionControllerImpl::ContextMenuTimerFired);
395 }
396 }
397
398 void TouchSelectionControllerImpl::HideContextMenu() {
399 context_menu_->SetVisible(false);
400 context_menu_timer_.Stop();
401 }
402
228 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() { 403 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() {
229 return selection_handle_1_->GetScreenPosition(); 404 return selection_handle_1_->GetScreenPosition();
230 } 405 }
231 406
232 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle2Position() { 407 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle2Position() {
233 return selection_handle_2_->GetScreenPosition(); 408 return selection_handle_2_->GetScreenPosition();
234 } 409 }
235 410
236 bool TouchSelectionControllerImpl::IsSelectionHandle1Visible() { 411 bool TouchSelectionControllerImpl::IsSelectionHandle1Visible() {
237 return selection_handle_1_->IsVisible(); 412 return selection_handle_1_->IsVisible();
238 } 413 }
239 414
240 bool TouchSelectionControllerImpl::IsSelectionHandle2Visible() { 415 bool TouchSelectionControllerImpl::IsSelectionHandle2Visible() {
241 return selection_handle_2_->IsVisible(); 416 return selection_handle_2_->IsVisible();
242 } 417 }
243 418
244 TouchSelectionController* TouchSelectionController::create( 419 TouchSelectionController* TouchSelectionController::create(
245 TouchSelectionClientView* client_view) { 420 TouchSelectionClientView* client_view) {
246 return new TouchSelectionControllerImpl(client_view); 421 return new TouchSelectionControllerImpl(client_view);
247 } 422 }
248 423
249 } // namespace views 424 } // namespace views
OLDNEW
« no previous file with comments | « views/touchui/touch_selection_controller_impl.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698