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

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: 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
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 RefreshButtonsAndLayout();
197 gfx::Rect widget_bounds(position.x() - width() / 2, position.y() - height(),
198 width(), height());
199 gfx::Rect monitor_bounds =
200 gfx::Screen::GetMonitorAreaNearestPoint(position);
201 widget_->SetBounds(widget_bounds.AdjustToFit(monitor_bounds));
sky 2011/08/26 15:45:33 Might you end up showing the context menu over the
varunjain 2011/08/26 16:31:15 This is possible in theory. But in practice, selec
202 }
203
204 gfx::Point GetScreenPosition() {
205 return widget_->GetClientAreaScreenBounds().origin();
206 }
207
208 // ButtonListener
209 virtual void ButtonPressed(Button* sender, const views::Event& event) {
210 controller_->ExecuteCommand(sender->tag());
211 }
212
213 private:
214 // Queries the client view for what elements to show in the menu and sizes
215 // the menu appropriately.
216 void RefreshButtonsAndLayout() {
sky 2011/08/26 15:45:33 nit: this doesn't layout, so maybe just RefreshBut
varunjain 2011/08/26 16:31:15 Done.
217 RemoveAllChildViews(true);
218 int total_width = 0;
219 int height = 0;
220 for (size_t i = 0; i < arraysize(kContextMenuCommands); i++) {
221 int command_id = kContextMenuCommands[i];
222 if (controller_->IsCommandIdEnabled(command_id)) {
223 TextButton* button = new TextButton(this,
224 UTF16ToWide(l10n_util::GetStringUTF16(command_id)));
225 button->set_focusable(true);
sky 2011/08/26 15:45:33 Is there a reason why we aren't using a standard m
226 button->set_request_focus_on_press(false);
227 button->set_prefix_type(TextButton::PREFIX_HIDE);
228 button->SetEnabledColor(SK_ColorWHITE);
229 button->SetHoverColor(SK_ColorWHITE);
230 button->set_background(
231 Background::CreateSolidBackground(SK_ColorBLACK));
232 button->set_alignment(TextButton::ALIGN_CENTER);
233 button->SetFont(ui::ResourceBundle::GetSharedInstance().GetFont(
234 ui::ResourceBundle::LargeFont));
235 button->set_tag(command_id);
236 AddChildView(button);
237 gfx::Size button_size = button->GetPreferredSize();
238 total_width += button_size.width() + kContextMenuPadding;
239 if (height < button_size.height())
240 height = button_size.height();
241 }
242 }
243 SetBounds(0, 0, total_width, height);
sky 2011/08/26 15:45:33 I don't think this is necessary. What matters is t
varunjain 2011/08/26 16:31:15 Done.
varunjain 2011/08/26 16:41:39 This is not really working. What I am seeing is th
244 }
245
246 scoped_ptr<Widget> widget_;
247 TouchSelectionControllerImpl* controller_;
248
249 DISALLOW_COPY_AND_ASSIGN(TouchContextMenuView);
250 };
251
144 TouchSelectionControllerImpl::TouchSelectionControllerImpl( 252 TouchSelectionControllerImpl::TouchSelectionControllerImpl(
145 TouchSelectionClientView* client_view) 253 TouchSelectionClientView* client_view)
146 : client_view_(client_view), 254 : client_view_(client_view),
147 selection_handle_1_(new SelectionHandleView(this)), 255 selection_handle_1_(new SelectionHandleView(this)),
148 selection_handle_2_(new SelectionHandleView(this)), 256 selection_handle_2_(new SelectionHandleView(this)),
257 context_menu_(new TouchContextMenuView(this)),
149 dragging_handle_(NULL) { 258 dragging_handle_(NULL) {
150 } 259 }
151 260
152 TouchSelectionControllerImpl::~TouchSelectionControllerImpl() { 261 TouchSelectionControllerImpl::~TouchSelectionControllerImpl() {
153 } 262 }
154 263
155 void TouchSelectionControllerImpl::SelectionChanged(const gfx::Point& p1, 264 void TouchSelectionControllerImpl::SelectionChanged(const gfx::Point& p1,
156 const gfx::Point& p2) { 265 const gfx::Point& p2) {
157 gfx::Point screen_pos_1(p1); 266 gfx::Point screen_pos_1(p1);
158 View::ConvertPointToScreen(client_view_, &screen_pos_1); 267 View::ConvertPointToScreen(client_view_, &screen_pos_1);
159 gfx::Point screen_pos_2(p2); 268 gfx::Point screen_pos_2(p2);
160 View::ConvertPointToScreen(client_view_, &screen_pos_2); 269 View::ConvertPointToScreen(client_view_, &screen_pos_2);
161 270
162 if (dragging_handle_) { 271 if (dragging_handle_) {
163 // We need to reposition only the selection handle that is being dragged. 272 // 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 273 // 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 274 // will always be at the end of selection, while the other handle will be at
166 // the start. 275 // the start.
167 dragging_handle_->SetScreenPosition(screen_pos_2); 276 dragging_handle_->SetScreenPosition(screen_pos_2);
168 } else { 277 } else {
278 UpdateContextMenu(p1, p2);
279
169 // Check if there is any selection at all. 280 // Check if there is any selection at all.
170 if (screen_pos_1 == screen_pos_2) { 281 if (screen_pos_1 == screen_pos_2) {
171 selection_handle_1_->SetVisible(false); 282 selection_handle_1_->SetVisible(false);
172 selection_handle_2_->SetVisible(false); 283 selection_handle_2_->SetVisible(false);
173 return; 284 return;
174 } 285 }
175 286
176 if (client_view_->bounds().Contains(p1)) { 287 if (client_view_->bounds().Contains(p1)) {
177 selection_handle_1_->SetScreenPosition(screen_pos_1); 288 selection_handle_1_->SetScreenPosition(screen_pos_1);
178 selection_handle_1_->SetVisible(true); 289 selection_handle_1_->SetVisible(true);
179 } else { 290 } else {
180 selection_handle_1_->SetVisible(false); 291 selection_handle_1_->SetVisible(false);
181 } 292 }
182 293
183 if (client_view_->bounds().Contains(p2)) { 294 if (client_view_->bounds().Contains(p2)) {
184 selection_handle_2_->SetScreenPosition(screen_pos_2); 295 selection_handle_2_->SetScreenPosition(screen_pos_2);
185 selection_handle_2_->SetVisible(true); 296 selection_handle_2_->SetVisible(true);
186 } else { 297 } else {
187 selection_handle_2_->SetVisible(false); 298 selection_handle_2_->SetVisible(false);
188 } 299 }
189 } 300 }
190 } 301 }
191 302
192 void TouchSelectionControllerImpl::ClientViewLostFocus() { 303 void TouchSelectionControllerImpl::ClientViewLostFocus() {
193 selection_handle_1_->SetVisible(false); 304 selection_handle_1_->SetVisible(false);
194 selection_handle_2_->SetVisible(false); 305 selection_handle_2_->SetVisible(false);
306 HideContextMenu();
195 } 307 }
196 308
197 void TouchSelectionControllerImpl::SelectionHandleDragged( 309 void TouchSelectionControllerImpl::SelectionHandleDragged(
198 const gfx::Point& drag_pos) { 310 const gfx::Point& drag_pos) {
311 // We do not want to show the context menu while dragging.
312 HideContextMenu();
313 context_menu_timer_.Start(
314 base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs),
315 this,
316 &TouchSelectionControllerImpl::ContextMenuTimerFired);
317
199 if (client_view_->GetWidget()) { 318 if (client_view_->GetWidget()) {
200 DCHECK(dragging_handle_); 319 DCHECK(dragging_handle_);
201 // Find the stationary selection handle. 320 // Find the stationary selection handle.
202 SelectionHandleView* fixed_handle = selection_handle_1_.get(); 321 SelectionHandleView* fixed_handle = selection_handle_1_.get();
203 if (fixed_handle == dragging_handle_) 322 if (fixed_handle == dragging_handle_)
204 fixed_handle = selection_handle_2_.get(); 323 fixed_handle = selection_handle_2_.get();
205 324
206 // Find selection end points in client_view's coordinate system. 325 // Find selection end points in client_view's coordinate system.
207 gfx::Point p1(drag_pos.x() + kSelectionHandleRadius, drag_pos.y()); 326 gfx::Point p1(drag_pos.x() + kSelectionHandleRadius, drag_pos.y());
208 ConvertPointToClientView(dragging_handle_, &p1); 327 ConvertPointToClientView(dragging_handle_, &p1);
209 328
210 gfx::Point p2(kSelectionHandleRadius, 0); 329 gfx::Point p2(kSelectionHandleRadius, 0);
211 ConvertPointToClientView(fixed_handle, &p2); 330 ConvertPointToClientView(fixed_handle, &p2);
212 331
213 // Instruct client_view to select the region between p1 and p2. The position 332 // 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 333 // of |fixed_handle| is the start and that of |dragging_handle| is the end
215 // of selection. 334 // of selection.
216 client_view_->SelectRect(p2, p1); 335 client_view_->SelectRect(p2, p1);
217 } 336 }
218 } 337 }
219 338
220 void TouchSelectionControllerImpl::ConvertPointToClientView( 339 void TouchSelectionControllerImpl::ConvertPointToClientView(
221 SelectionHandleView* source, gfx::Point* point) { 340 SelectionHandleView* source, gfx::Point* point) {
222 View::ConvertPointToScreen(source, point); 341 View::ConvertPointToScreen(source, point);
223 gfx::Rect r = client_view_->GetWidget()->GetClientAreaScreenBounds(); 342 gfx::Rect r = client_view_->GetWidget()->GetClientAreaScreenBounds();
224 point->SetPoint(point->x() - r.x(), point->y() - r.y()); 343 point->SetPoint(point->x() - r.x(), point->y() - r.y());
225 View::ConvertPointFromWidget(client_view_, point); 344 View::ConvertPointFromWidget(client_view_, point);
226 } 345 }
227 346
347 bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const {
348 return client_view_->IsCommandIdEnabled(command_id);
349 }
350
351 void TouchSelectionControllerImpl::ExecuteCommand(int command_id) {
352 HideContextMenu();
353 client_view_->ExecuteCommand(command_id);
354 }
355
356 void TouchSelectionControllerImpl::ContextMenuTimerFired() {
357 context_menu_->SetScreenPosition(context_menu_screen_pos_);
358 context_menu_->SetVisible(true);
359 }
360
361 void TouchSelectionControllerImpl::UpdateContextMenu(const gfx::Point& p1,
362 const gfx::Point& p2) {
363
364 // No selection means no context menu.
365 if (p1 == p2) {
366 HideContextMenu();
367 return;
368 }
369
370 // if selection is completely inside the view, we display the context menu
371 // in the middle of the end points on the top. Else, we show the menu on the
372 // top border of the view in the center.
373 if (client_view_->bounds().Contains(p1) &&
374 client_view_->bounds().Contains(p2)) {
375 context_menu_screen_pos_.set_x((p1.x() + p2.x()) / 2);
sky 2011/08/26 15:45:33 Why does this need to be cached here instead of ca
varunjain 2011/08/26 16:31:15 It doesnt. Changed.
376 context_menu_screen_pos_.set_y(
377 std::min(p1.y(), p2.y()) - kContextMenuVerticalOffset);
378 } else {
379 context_menu_screen_pos_.set_x(
380 client_view_->x() + client_view_->width() / 2);
381 context_menu_screen_pos_.set_y(client_view_->y());
382 }
383
384 View::ConvertPointToScreen(client_view_, &context_menu_screen_pos_);
385
386 // If menu is already visible we hide it to be display after the timer fires.
387 if (context_menu_->IsVisible())
388 context_menu_->SetVisible(false);
389 if (context_menu_timer_.IsRunning()) {
390 context_menu_timer_.Reset();
391 } else {
392 context_menu_timer_.Start(
393 base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs),
394 this,
395 &TouchSelectionControllerImpl::ContextMenuTimerFired);
396 }
397 }
398
399 void TouchSelectionControllerImpl::HideContextMenu() {
400 context_menu_->SetVisible(false);
401 if (context_menu_timer_.IsRunning())
sky 2011/08/26 15:45:33 No need to check if running.
varunjain 2011/08/26 16:31:15 Done.
402 context_menu_timer_.Stop();
403 }
404
228 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() { 405 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() {
229 return selection_handle_1_->GetScreenPosition(); 406 return selection_handle_1_->GetScreenPosition();
230 } 407 }
231 408
232 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle2Position() { 409 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle2Position() {
233 return selection_handle_2_->GetScreenPosition(); 410 return selection_handle_2_->GetScreenPosition();
234 } 411 }
235 412
236 bool TouchSelectionControllerImpl::IsSelectionHandle1Visible() { 413 bool TouchSelectionControllerImpl::IsSelectionHandle1Visible() {
237 return selection_handle_1_->IsVisible(); 414 return selection_handle_1_->IsVisible();
238 } 415 }
239 416
240 bool TouchSelectionControllerImpl::IsSelectionHandle2Visible() { 417 bool TouchSelectionControllerImpl::IsSelectionHandle2Visible() {
241 return selection_handle_2_->IsVisible(); 418 return selection_handle_2_->IsVisible();
242 } 419 }
243 420
244 TouchSelectionController* TouchSelectionController::create( 421 TouchSelectionController* TouchSelectionController::create(
245 TouchSelectionClientView* client_view) { 422 TouchSelectionClientView* client_view) {
246 return new TouchSelectionControllerImpl(client_view); 423 return new TouchSelectionControllerImpl(client_view);
247 } 424 }
248 425
249 } // namespace views 426 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698