| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "views/touchui/touch_selection_controller_impl.h" | |
| 6 | |
| 7 #include "base/time.h" | |
| 8 #include "base/utf_string_conversions.h" | |
| 9 #include "grit/ui_strings.h" | |
| 10 #include "third_party/skia/include/effects/SkGradientShader.h" | |
| 11 #include "ui/base/l10n/l10n_util.h" | |
| 12 #include "ui/base/resource/resource_bundle.h" | |
| 13 #include "ui/gfx/canvas.h" | |
| 14 #include "ui/gfx/canvas_skia.h" | |
| 15 #include "ui/gfx/path.h" | |
| 16 #include "ui/gfx/rect.h" | |
| 17 #include "ui/gfx/screen.h" | |
| 18 #include "ui/gfx/size.h" | |
| 19 #include "ui/gfx/transform.h" | |
| 20 #include "views/background.h" | |
| 21 #include "views/controls/button/button.h" | |
| 22 #include "views/controls/button/custom_button.h" | |
| 23 #include "views/controls/button/text_button.h" | |
| 24 #include "views/controls/label.h" | |
| 25 #include "views/controls/menu/menu_config.h" | |
| 26 #include "views/layout/box_layout.h" | |
| 27 #include "views/widget/widget.h" | |
| 28 | |
| 29 namespace { | |
| 30 | |
| 31 // Constants defining the visual attributes of selection handles | |
| 32 const int kSelectionHandleRadius = 10; | |
| 33 const int kSelectionHandleCursorHeight = 10; | |
| 34 const int kSelectionHandleAlpha = 0x7F; | |
| 35 const SkColor kSelectionHandleColor = | |
| 36 SkColorSetA(SK_ColorBLUE, kSelectionHandleAlpha); | |
| 37 | |
| 38 // The minimum selection size to trigger selection controller. | |
| 39 const int kMinSelectionSize = 4; | |
| 40 | |
| 41 const int kContextMenuCommands[] = {IDS_APP_CUT, | |
| 42 IDS_APP_COPY, | |
| 43 // TODO(varunjain): PASTE is acting funny due to some gtk clipboard issue. | |
| 44 // Uncomment the following when that is fixed. | |
| 45 // IDS_APP_PASTE, | |
| 46 IDS_APP_DELETE, | |
| 47 IDS_APP_SELECT_ALL}; | |
| 48 const int kContextMenuPadding = 2; | |
| 49 const int kContextMenuTimoutMs = 1000; | |
| 50 const int kContextMenuVerticalOffset = 25; | |
| 51 | |
| 52 // Convenience struct to represent a circle shape. | |
| 53 struct Circle { | |
| 54 int radius; | |
| 55 gfx::Point center; | |
| 56 SkColor color; | |
| 57 }; | |
| 58 | |
| 59 // Creates a widget to host SelectionHandleView. | |
| 60 views::Widget* CreateTouchSelectionPopupWidget() { | |
| 61 views::Widget* widget = new views::Widget; | |
| 62 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); | |
| 63 params.can_activate = false; | |
| 64 params.transparent = true; | |
| 65 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 66 widget->Init(params); | |
| 67 return widget; | |
| 68 } | |
| 69 | |
| 70 void PaintCircle(const Circle& circle, gfx::Canvas* canvas) { | |
| 71 SkPaint paint; | |
| 72 paint.setAntiAlias(true); | |
| 73 paint.setStyle(SkPaint::kFill_Style); | |
| 74 paint.setColor(circle.color); | |
| 75 gfx::Path path; | |
| 76 gfx::Rect bounds(circle.center.x() - circle.radius, | |
| 77 circle.center.y() - circle.radius, | |
| 78 circle.radius * 2, | |
| 79 circle.radius * 2); | |
| 80 SkRect rect; | |
| 81 rect.set(SkIntToScalar(bounds.x()), SkIntToScalar(bounds.y()), | |
| 82 SkIntToScalar(bounds.right()), SkIntToScalar(bounds.bottom())); | |
| 83 SkScalar radius = SkIntToScalar(circle.radius); | |
| 84 path.addRoundRect(rect, radius, radius); | |
| 85 canvas->GetSkCanvas()->drawPath(path, paint); | |
| 86 } | |
| 87 | |
| 88 // The points may not match exactly, since the selection range computation may | |
| 89 // introduce some floating point errors. So check for a minimum size to decide | |
| 90 // whether or not there is any selection. | |
| 91 bool IsEmptySelection(const gfx::Point& p1, const gfx::Point& p2) { | |
| 92 int delta_x = p2.x() - p1.x(); | |
| 93 int delta_y = p2.y() - p1.y(); | |
| 94 return (abs(delta_x) < kMinSelectionSize && abs(delta_y) < kMinSelectionSize); | |
| 95 } | |
| 96 | |
| 97 } // namespace | |
| 98 | |
| 99 namespace views { | |
| 100 | |
| 101 // A View that displays the text selection handle. | |
| 102 class TouchSelectionControllerImpl::SelectionHandleView : public View { | |
| 103 public: | |
| 104 SelectionHandleView(TouchSelectionControllerImpl* controller) | |
| 105 : controller_(controller) { | |
| 106 widget_.reset(CreateTouchSelectionPopupWidget()); | |
| 107 widget_->SetContentsView(this); | |
| 108 widget_->SetAlwaysOnTop(true); | |
| 109 | |
| 110 // We are owned by the TouchSelectionController. | |
| 111 set_parent_owned(false); | |
| 112 } | |
| 113 | |
| 114 virtual ~SelectionHandleView() { | |
| 115 } | |
| 116 | |
| 117 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { | |
| 118 Circle circle = {kSelectionHandleRadius, gfx::Point(kSelectionHandleRadius, | |
| 119 kSelectionHandleRadius + kSelectionHandleCursorHeight), | |
| 120 kSelectionHandleColor}; | |
| 121 PaintCircle(circle, canvas); | |
| 122 canvas->DrawLineInt(kSelectionHandleColor, kSelectionHandleRadius, 0, | |
| 123 kSelectionHandleRadius, kSelectionHandleCursorHeight); | |
| 124 } | |
| 125 | |
| 126 virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE { | |
| 127 controller_->dragging_handle_ = this; | |
| 128 return true; | |
| 129 } | |
| 130 | |
| 131 virtual bool OnMouseDragged(const MouseEvent& event) OVERRIDE { | |
| 132 controller_->SelectionHandleDragged(event.location()); | |
| 133 return true; | |
| 134 } | |
| 135 | |
| 136 virtual void OnMouseReleased(const MouseEvent& event) OVERRIDE { | |
| 137 controller_->dragging_handle_ = NULL; | |
| 138 } | |
| 139 | |
| 140 virtual void OnMouseCaptureLost() OVERRIDE { | |
| 141 controller_->dragging_handle_ = NULL; | |
| 142 } | |
| 143 | |
| 144 virtual void SetVisible(bool visible) OVERRIDE { | |
| 145 // We simply show/hide the container widget. | |
| 146 if (visible != widget_->IsVisible()) { | |
| 147 if (visible) | |
| 148 widget_->Show(); | |
| 149 else | |
| 150 widget_->Hide(); | |
| 151 } | |
| 152 View::SetVisible(visible); | |
| 153 } | |
| 154 | |
| 155 virtual gfx::Size GetPreferredSize() OVERRIDE { | |
| 156 return gfx::Size(2 * kSelectionHandleRadius, | |
| 157 2 * kSelectionHandleRadius + kSelectionHandleCursorHeight); | |
| 158 } | |
| 159 | |
| 160 void SetScreenPosition(const gfx::Point& position) { | |
| 161 gfx::Rect widget_bounds(position.x() - kSelectionHandleRadius, position.y(), | |
| 162 2 * kSelectionHandleRadius, | |
| 163 2 * kSelectionHandleRadius + kSelectionHandleCursorHeight); | |
| 164 widget_->SetBounds(widget_bounds); | |
| 165 } | |
| 166 | |
| 167 gfx::Point GetScreenPosition() { | |
| 168 return widget_->GetClientAreaScreenBounds().origin(); | |
| 169 } | |
| 170 | |
| 171 private: | |
| 172 scoped_ptr<Widget> widget_; | |
| 173 TouchSelectionControllerImpl* controller_; | |
| 174 | |
| 175 DISALLOW_COPY_AND_ASSIGN(SelectionHandleView); | |
| 176 }; | |
| 177 | |
| 178 class ContextMenuButtonBackground : public Background { | |
| 179 public: | |
| 180 ContextMenuButtonBackground() {} | |
| 181 | |
| 182 virtual void Paint(gfx::Canvas* canvas, View* view) const OVERRIDE { | |
| 183 CustomButton::ButtonState state = static_cast<CustomButton*>(view)->state(); | |
| 184 SkColor background_color, border_color; | |
| 185 if (state == CustomButton::BS_NORMAL) { | |
| 186 background_color = SkColorSetARGB(102, 255, 255, 255); | |
| 187 border_color = SkColorSetARGB(36, 0, 0, 0); | |
| 188 } else { | |
| 189 background_color = SkColorSetARGB(13, 0, 0, 0); | |
| 190 border_color = SkColorSetARGB(72, 0, 0, 0); | |
| 191 } | |
| 192 int w = view->width(); | |
| 193 int h = view->height(); | |
| 194 canvas->FillRect(background_color, gfx::Rect(1, 1, w - 2, h - 2)); | |
| 195 canvas->FillRect(border_color, gfx::Rect(2, 0, w - 4, 1)); | |
| 196 canvas->FillRect(border_color, gfx::Rect(1, 1, 1, 1)); | |
| 197 canvas->FillRect(border_color, gfx::Rect(0, 2, 1, h - 4)); | |
| 198 canvas->FillRect(border_color, gfx::Rect(1, h - 2, 1, 1)); | |
| 199 canvas->FillRect(border_color, gfx::Rect(2, h - 1, w - 4, 1)); | |
| 200 canvas->FillRect(border_color, gfx::Rect(w - 2, 1, 1, 1)); | |
| 201 canvas->FillRect(border_color, gfx::Rect(w - 1, 2, 1, h - 4)); | |
| 202 canvas->FillRect(border_color, gfx::Rect(w - 2, h - 2, 1, 1)); | |
| 203 } | |
| 204 | |
| 205 private: | |
| 206 DISALLOW_COPY_AND_ASSIGN(ContextMenuButtonBackground); | |
| 207 }; | |
| 208 | |
| 209 // A View that displays the touch context menu. | |
| 210 class TouchSelectionControllerImpl::TouchContextMenuView | |
| 211 : public ButtonListener, | |
| 212 public View { | |
| 213 public: | |
| 214 TouchContextMenuView(TouchSelectionControllerImpl* controller) | |
| 215 : controller_(controller) { | |
| 216 widget_.reset(CreateTouchSelectionPopupWidget()); | |
| 217 widget_->SetContentsView(this); | |
| 218 widget_->SetAlwaysOnTop(true); | |
| 219 | |
| 220 // We are owned by the TouchSelectionController. | |
| 221 set_parent_owned(false); | |
| 222 SetLayoutManager(new BoxLayout(BoxLayout::kHorizontal, kContextMenuPadding, | |
| 223 kContextMenuPadding, kContextMenuPadding)); | |
| 224 } | |
| 225 | |
| 226 virtual ~TouchContextMenuView() { | |
| 227 } | |
| 228 | |
| 229 virtual void SetVisible(bool visible) OVERRIDE { | |
| 230 // We simply show/hide the container widget. | |
| 231 if (visible != widget_->IsVisible()) { | |
| 232 if (visible) | |
| 233 widget_->Show(); | |
| 234 else | |
| 235 widget_->Hide(); | |
| 236 } | |
| 237 View::SetVisible(visible); | |
| 238 } | |
| 239 | |
| 240 void SetScreenPosition(const gfx::Point& position) { | |
| 241 RefreshButtonsAndSetWidgetPosition(position); | |
| 242 } | |
| 243 | |
| 244 gfx::Point GetScreenPosition() { | |
| 245 return widget_->GetClientAreaScreenBounds().origin(); | |
| 246 } | |
| 247 | |
| 248 void OnPaintBackground(gfx::Canvas* canvas) OVERRIDE { | |
| 249 // TODO(varunjain): the following color scheme is copied from | |
| 250 // menu_scroll_view_container.cc. Figure out how to consolidate the two | |
| 251 // pieces of code. | |
| 252 #if defined(OS_CHROMEOS) | |
| 253 static const SkColor kGradientColors[2] = { | |
| 254 SK_ColorWHITE, | |
| 255 SkColorSetRGB(0xF0, 0xF0, 0xF0) | |
| 256 }; | |
| 257 | |
| 258 static const SkScalar kGradientPoints[2] = { | |
| 259 SkIntToScalar(0), | |
| 260 SkIntToScalar(1) | |
| 261 }; | |
| 262 | |
| 263 SkPoint points[2]; | |
| 264 points[0].set(SkIntToScalar(0), SkIntToScalar(0)); | |
| 265 points[1].set(SkIntToScalar(0), SkIntToScalar(height())); | |
| 266 | |
| 267 SkShader* shader = SkGradientShader::CreateLinear(points, | |
| 268 kGradientColors, kGradientPoints, arraysize(kGradientPoints), | |
| 269 SkShader::kRepeat_TileMode); | |
| 270 DCHECK(shader); | |
| 271 | |
| 272 SkPaint paint; | |
| 273 paint.setShader(shader); | |
| 274 shader->unref(); | |
| 275 | |
| 276 paint.setStyle(SkPaint::kFill_Style); | |
| 277 paint.setXfermodeMode(SkXfermode::kSrc_Mode); | |
| 278 | |
| 279 canvas->DrawRectInt(0, 0, width(), height(), paint); | |
| 280 #else | |
| 281 // This is the same as COLOR_TOOLBAR. | |
| 282 canvas->GetSkCanvas()->drawColor(SkColorSetRGB(210, 225, 246), | |
| 283 SkXfermode::kSrc_Mode); | |
| 284 #endif | |
| 285 } | |
| 286 | |
| 287 // ButtonListener | |
| 288 virtual void ButtonPressed(Button* sender, const views::Event& event) { | |
| 289 controller_->ExecuteCommand(sender->tag()); | |
| 290 } | |
| 291 | |
| 292 private: | |
| 293 // Queries the client view for what elements to show in the menu and sizes | |
| 294 // the menu appropriately. | |
| 295 void RefreshButtonsAndSetWidgetPosition(const gfx::Point& position) { | |
| 296 RemoveAllChildViews(true); | |
| 297 int total_width = 0; | |
| 298 int height = 0; | |
| 299 for (size_t i = 0; i < arraysize(kContextMenuCommands); i++) { | |
| 300 int command_id = kContextMenuCommands[i]; | |
| 301 if (controller_->IsCommandIdEnabled(command_id)) { | |
| 302 TextButton* button = new TextButton( | |
| 303 this, l10n_util::GetStringUTF16(command_id)); | |
| 304 button->set_focusable(true); | |
| 305 button->set_request_focus_on_press(false); | |
| 306 button->set_prefix_type(TextButton::PREFIX_HIDE); | |
| 307 button->SetEnabledColor(MenuConfig::instance().text_color); | |
| 308 button->set_background(new ContextMenuButtonBackground()); | |
| 309 button->set_alignment(TextButton::ALIGN_CENTER); | |
| 310 button->SetFont(ui::ResourceBundle::GetSharedInstance().GetFont( | |
| 311 ui::ResourceBundle::LargeFont)); | |
| 312 button->set_tag(command_id); | |
| 313 AddChildView(button); | |
| 314 gfx::Size button_size = button->GetPreferredSize(); | |
| 315 total_width += button_size.width() + kContextMenuPadding; | |
| 316 if (height < button_size.height()) | |
| 317 height = button_size.height(); | |
| 318 } | |
| 319 } | |
| 320 gfx::Rect widget_bounds(position.x() - total_width / 2, | |
| 321 position.y() - height, | |
| 322 total_width, | |
| 323 height); | |
| 324 gfx::Rect monitor_bounds = | |
| 325 gfx::Screen::GetMonitorAreaNearestPoint(position); | |
| 326 widget_->SetBounds(widget_bounds.AdjustToFit(monitor_bounds)); | |
| 327 Layout(); | |
| 328 } | |
| 329 | |
| 330 scoped_ptr<Widget> widget_; | |
| 331 TouchSelectionControllerImpl* controller_; | |
| 332 | |
| 333 DISALLOW_COPY_AND_ASSIGN(TouchContextMenuView); | |
| 334 }; | |
| 335 | |
| 336 TouchSelectionControllerImpl::TouchSelectionControllerImpl( | |
| 337 TouchSelectionClientView* client_view) | |
| 338 : client_view_(client_view), | |
| 339 selection_handle_1_(new SelectionHandleView(this)), | |
| 340 selection_handle_2_(new SelectionHandleView(this)), | |
| 341 context_menu_(new TouchContextMenuView(this)), | |
| 342 dragging_handle_(NULL) { | |
| 343 } | |
| 344 | |
| 345 TouchSelectionControllerImpl::~TouchSelectionControllerImpl() { | |
| 346 } | |
| 347 | |
| 348 void TouchSelectionControllerImpl::SelectionChanged(const gfx::Point& p1, | |
| 349 const gfx::Point& p2) { | |
| 350 gfx::Point screen_pos_1(p1); | |
| 351 View::ConvertPointToScreen(client_view_, &screen_pos_1); | |
| 352 gfx::Point screen_pos_2(p2); | |
| 353 View::ConvertPointToScreen(client_view_, &screen_pos_2); | |
| 354 | |
| 355 if (dragging_handle_) { | |
| 356 // We need to reposition only the selection handle that is being dragged. | |
| 357 // The other handle stays the same. Also, the selection handle being dragged | |
| 358 // will always be at the end of selection, while the other handle will be at | |
| 359 // the start. | |
| 360 dragging_handle_->SetScreenPosition(screen_pos_2); | |
| 361 } else { | |
| 362 UpdateContextMenu(p1, p2); | |
| 363 | |
| 364 // Check if there is any selection at all. | |
| 365 if (IsEmptySelection(screen_pos_2, screen_pos_1)) { | |
| 366 selection_handle_1_->SetVisible(false); | |
| 367 selection_handle_2_->SetVisible(false); | |
| 368 return; | |
| 369 } | |
| 370 | |
| 371 if (client_view_->bounds().Contains(p1)) { | |
| 372 selection_handle_1_->SetScreenPosition(screen_pos_1); | |
| 373 selection_handle_1_->SetVisible(true); | |
| 374 } else { | |
| 375 selection_handle_1_->SetVisible(false); | |
| 376 } | |
| 377 | |
| 378 if (client_view_->bounds().Contains(p2)) { | |
| 379 selection_handle_2_->SetScreenPosition(screen_pos_2); | |
| 380 selection_handle_2_->SetVisible(true); | |
| 381 } else { | |
| 382 selection_handle_2_->SetVisible(false); | |
| 383 } | |
| 384 } | |
| 385 } | |
| 386 | |
| 387 void TouchSelectionControllerImpl::ClientViewLostFocus() { | |
| 388 selection_handle_1_->SetVisible(false); | |
| 389 selection_handle_2_->SetVisible(false); | |
| 390 HideContextMenu(); | |
| 391 } | |
| 392 | |
| 393 void TouchSelectionControllerImpl::SelectionHandleDragged( | |
| 394 const gfx::Point& drag_pos) { | |
| 395 // We do not want to show the context menu while dragging. | |
| 396 HideContextMenu(); | |
| 397 context_menu_timer_.Start( | |
| 398 FROM_HERE, | |
| 399 base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs), | |
| 400 this, | |
| 401 &TouchSelectionControllerImpl::ContextMenuTimerFired); | |
| 402 | |
| 403 if (client_view_->GetWidget()) { | |
| 404 DCHECK(dragging_handle_); | |
| 405 // Find the stationary selection handle. | |
| 406 SelectionHandleView* fixed_handle = selection_handle_1_.get(); | |
| 407 if (fixed_handle == dragging_handle_) | |
| 408 fixed_handle = selection_handle_2_.get(); | |
| 409 | |
| 410 // Find selection end points in client_view's coordinate system. | |
| 411 gfx::Point p1(drag_pos.x() + kSelectionHandleRadius, drag_pos.y()); | |
| 412 ConvertPointToClientView(dragging_handle_, &p1); | |
| 413 | |
| 414 gfx::Point p2(kSelectionHandleRadius, 0); | |
| 415 ConvertPointToClientView(fixed_handle, &p2); | |
| 416 | |
| 417 // Instruct client_view to select the region between p1 and p2. The position | |
| 418 // of |fixed_handle| is the start and that of |dragging_handle| is the end | |
| 419 // of selection. | |
| 420 client_view_->SelectRect(p2, p1); | |
| 421 } | |
| 422 } | |
| 423 | |
| 424 void TouchSelectionControllerImpl::ConvertPointToClientView( | |
| 425 SelectionHandleView* source, gfx::Point* point) { | |
| 426 View::ConvertPointToScreen(source, point); | |
| 427 gfx::Rect r = client_view_->GetWidget()->GetClientAreaScreenBounds(); | |
| 428 point->SetPoint(point->x() - r.x(), point->y() - r.y()); | |
| 429 View::ConvertPointFromWidget(client_view_, point); | |
| 430 } | |
| 431 | |
| 432 bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const { | |
| 433 return client_view_->IsCommandIdEnabled(command_id); | |
| 434 } | |
| 435 | |
| 436 void TouchSelectionControllerImpl::ExecuteCommand(int command_id) { | |
| 437 HideContextMenu(); | |
| 438 client_view_->ExecuteCommand(command_id); | |
| 439 } | |
| 440 | |
| 441 void TouchSelectionControllerImpl::ContextMenuTimerFired() { | |
| 442 // Get selection end points in client_view's space. | |
| 443 gfx::Point p1(kSelectionHandleRadius, 0); | |
| 444 ConvertPointToClientView(selection_handle_1_.get(), &p1); | |
| 445 gfx::Point p2(kSelectionHandleRadius, 0); | |
| 446 ConvertPointToClientView(selection_handle_2_.get(), &p2); | |
| 447 | |
| 448 // if selection is completely inside the view, we display the context menu | |
| 449 // in the middle of the end points on the top. Else, we show the menu on the | |
| 450 // top border of the view in the center. | |
| 451 gfx::Point menu_pos; | |
| 452 if (client_view_->bounds().Contains(p1) && | |
| 453 client_view_->bounds().Contains(p2)) { | |
| 454 menu_pos.set_x((p1.x() + p2.x()) / 2); | |
| 455 menu_pos.set_y(std::min(p1.y(), p2.y()) - kContextMenuVerticalOffset); | |
| 456 } else { | |
| 457 menu_pos.set_x(client_view_->x() + client_view_->width() / 2); | |
| 458 menu_pos.set_y(client_view_->y()); | |
| 459 } | |
| 460 | |
| 461 View::ConvertPointToScreen(client_view_, &menu_pos); | |
| 462 | |
| 463 context_menu_->SetScreenPosition(menu_pos); | |
| 464 context_menu_->SetVisible(true); | |
| 465 } | |
| 466 | |
| 467 void TouchSelectionControllerImpl::UpdateContextMenu(const gfx::Point& p1, | |
| 468 const gfx::Point& p2) { | |
| 469 // Hide context menu to be shown when the timer fires. | |
| 470 HideContextMenu(); | |
| 471 | |
| 472 // If there is selection, we restart the context menu timer. | |
| 473 if (!IsEmptySelection(p1, p2)) { | |
| 474 context_menu_timer_.Start( | |
| 475 FROM_HERE, | |
| 476 base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs), | |
| 477 this, | |
| 478 &TouchSelectionControllerImpl::ContextMenuTimerFired); | |
| 479 } | |
| 480 } | |
| 481 | |
| 482 void TouchSelectionControllerImpl::HideContextMenu() { | |
| 483 context_menu_->SetVisible(false); | |
| 484 context_menu_timer_.Stop(); | |
| 485 } | |
| 486 | |
| 487 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() { | |
| 488 return selection_handle_1_->GetScreenPosition(); | |
| 489 } | |
| 490 | |
| 491 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle2Position() { | |
| 492 return selection_handle_2_->GetScreenPosition(); | |
| 493 } | |
| 494 | |
| 495 bool TouchSelectionControllerImpl::IsSelectionHandle1Visible() { | |
| 496 return selection_handle_1_->IsVisible(); | |
| 497 } | |
| 498 | |
| 499 bool TouchSelectionControllerImpl::IsSelectionHandle2Visible() { | |
| 500 return selection_handle_2_->IsVisible(); | |
| 501 } | |
| 502 | |
| 503 TouchSelectionController* TouchSelectionController::create( | |
| 504 TouchSelectionClientView* client_view) { | |
| 505 return new TouchSelectionControllerImpl(client_view); | |
| 506 } | |
| 507 | |
| 508 } // namespace views | |
| OLD | NEW |