diff options
author | varunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-03 01:19:10 +0000 |
---|---|---|
committer | varunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-03 01:19:10 +0000 |
commit | 4a3f5a60151cb476f4dc6740606e798cf8b823a9 (patch) | |
tree | 8b9d127a1540c0a1f532ddffb737850012736530 /views/touchui | |
parent | a06da298d68e1bd4e3f7f25f90d4786b38a10caf (diff) | |
download | chromium_src-4a3f5a60151cb476f4dc6740606e798cf8b823a9.zip chromium_src-4a3f5a60151cb476f4dc6740606e798cf8b823a9.tar.gz chromium_src-4a3f5a60151cb476f4dc6740606e798cf8b823a9.tar.bz2 |
Implement text selection widgets for controlling text selection on touch
interface. We want this functionality on views textfields as well as the
webpage. This CL lays out the ground work and implements the text selection
interface for view textfield. In a later CL, I intend to implement the
TextSelection Interface for RenderWidgetHostViewViews.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/7465045
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@95186 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views/touchui')
-rw-r--r-- | views/touchui/touch_selection_controller.cc | 16 | ||||
-rw-r--r-- | views/touchui/touch_selection_controller.h | 56 | ||||
-rw-r--r-- | views/touchui/touch_selection_controller_impl.cc | 240 | ||||
-rw-r--r-- | views/touchui/touch_selection_controller_impl.h | 53 |
4 files changed, 365 insertions, 0 deletions
diff --git a/views/touchui/touch_selection_controller.cc b/views/touchui/touch_selection_controller.cc new file mode 100644 index 0000000..6d0a69a --- /dev/null +++ b/views/touchui/touch_selection_controller.cc @@ -0,0 +1,16 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "views/touchui/touch_selection_controller.h" + +namespace views { + +#if !defined(TOUCH_UI) +TouchSelectionController* TouchSelectionController::create( + TouchSelectionClientView* client_view) { + return NULL; +} +#endif + +} // namespace views. diff --git a/views/touchui/touch_selection_controller.h b/views/touchui/touch_selection_controller.h new file mode 100644 index 0000000..ce028e0 --- /dev/null +++ b/views/touchui/touch_selection_controller.h @@ -0,0 +1,56 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_H_ +#define VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_H_ +#pragma once + +#include "ui/base/models/simple_menu_model.h" +#include "ui/gfx/point.h" +#include "views/view.h" + +namespace views { + +// An interface implemented by a View that has text that can be selected. +class VIEWS_API TouchSelectionClientView + : public View, + public ui::SimpleMenuModel::Delegate { + public: + // Select everything between start and end (points are in view's local + // coordinate system). |start| is the logical start and |end| is the logical + // end of selection. Visually, |start| may lie after |end|. + virtual void SelectRect(const gfx::Point& start, const gfx::Point& end) = 0; + + protected: + virtual ~TouchSelectionClientView() {} +}; + +// This defines the callback interface for other code to be notified of changes +// in the state of a TouchSelectionClientView. +class VIEWS_API TouchSelectionController { + public: + virtual ~TouchSelectionController() {} + + // Creates a TouchSelectionController. Caller owns the returned object. + static TouchSelectionController* create( + TouchSelectionClientView* client_view); + + // Notification that the text selection in TouchSelectionClientView has + // changed. p1 and p2 are lower corners of the start and end of selection: + // ____________________________________ + // | textfield with |selected text| | + // ------------------------------------ + // ^p1 ^p2 + // + // p1 is always the start and p2 is always the end of selection. Hence, + // p1 could be to the right of p2 in the figure above. + virtual void SelectionChanged(const gfx::Point& p1, const gfx::Point& p2) = 0; + + // Notification that the TouchSelectionClientView has lost focus. + virtual void ClientViewLostFocus() = 0; +}; + +} // namespace views + +#endif // VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_H_ diff --git a/views/touchui/touch_selection_controller_impl.cc b/views/touchui/touch_selection_controller_impl.cc new file mode 100644 index 0000000..0e965d2 --- /dev/null +++ b/views/touchui/touch_selection_controller_impl.cc @@ -0,0 +1,240 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "views/touchui/touch_selection_controller_impl.h" + +#include "ui/gfx/canvas.h" +#include "ui/gfx/canvas_skia.h" +#include "ui/gfx/path.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/size.h" +#include "ui/gfx/transform.h" +#include "views/widget/widget.h" +#include "views/controls/label.h" +#include "views/background.h" + +namespace { + +// Constants defining the visual attributes of selection handles +const int kSelectionHandleRadius = 10; +const int kSelectionHandleCursorHeight = 10; +const int kSelectionHandleAlpha = 0x7F; +const SkColor kSelectionHandleColor = + SkColorSetA(SK_ColorBLUE, kSelectionHandleAlpha); + +// Convenience struct to represent a circle shape. +struct Circle { + int radius; + gfx::Point center; + SkColor color; +}; + +// Creates a widget to host SelectionHandleView. +views::Widget* CreateSelectionHandleWidget() { + views::Widget* widget = new views::Widget; + views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); + params.can_activate = false; + params.transparent = true; + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget->Init(params); + return widget; +} + +void PaintCircle(const Circle& circle, gfx::Canvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(circle.color); + gfx::Path path; + gfx::Rect bounds(circle.center.x() - circle.radius, + circle.center.y() - circle.radius, + circle.radius * 2, + circle.radius * 2); + SkRect rect; + rect.set(SkIntToScalar(bounds.x()), SkIntToScalar(bounds.y()), + SkIntToScalar(bounds.right()), SkIntToScalar(bounds.bottom())); + SkScalar radius = SkIntToScalar(circle.radius); + path.addRoundRect(rect, radius, radius); + canvas->AsCanvasSkia()->drawPath(path, paint); +} + +} // namespace + +namespace views { + +// A View that displays the text selection handle. +class TouchSelectionControllerImpl::SelectionHandleView : public View { + public: + SelectionHandleView(TouchSelectionControllerImpl* controller) + : controller_(controller) { + widget_.reset(CreateSelectionHandleWidget()); + widget_->SetContentsView(this); + widget_->SetAlwaysOnTop(true); + + // We are owned by the TouchSelectionController. + set_parent_owned(false); + } + + virtual ~SelectionHandleView() { + widget_->Close(); + } + + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { + Circle circle = {kSelectionHandleRadius, gfx::Point(kSelectionHandleRadius, + kSelectionHandleRadius + kSelectionHandleCursorHeight), + kSelectionHandleColor}; + PaintCircle(circle, canvas); + canvas->DrawLineInt(kSelectionHandleColor, kSelectionHandleRadius, 0, + kSelectionHandleRadius, kSelectionHandleCursorHeight); + } + + virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE { + controller_->dragging_handle_ = this; + return true; + } + + virtual bool OnMouseDragged(const MouseEvent& event) OVERRIDE { + controller_->SelectionHandleDragged(event.x()); + return true; + } + + virtual void OnMouseReleased(const MouseEvent& event) OVERRIDE { + controller_->dragging_handle_ = NULL; + } + + virtual void OnMouseCaptureLost() OVERRIDE { + controller_->dragging_handle_ = NULL; + } + + virtual void SetVisible(bool visible) OVERRIDE { + // We simply show/hide the container widget. + if (visible != widget_->IsVisible()) { + if (visible) + widget_->Show(); + else + widget_->Hide(); + } + View::SetVisible(visible); + } + + virtual gfx::Size GetPreferredSize() OVERRIDE { + return gfx::Size(2 * kSelectionHandleRadius, + 2 * kSelectionHandleRadius + kSelectionHandleCursorHeight); + } + + void SetScreenPosition(const gfx::Point& position) { + gfx::Rect widget_bounds(position.x() - kSelectionHandleRadius, position.y(), + 2 * kSelectionHandleRadius, + 2 * kSelectionHandleRadius + kSelectionHandleCursorHeight); + widget_->SetBounds(widget_bounds); + } + + gfx::Point GetScreenPosition() { + return widget_->GetClientAreaScreenBounds().origin(); + } + + private: + scoped_ptr<Widget> widget_; + TouchSelectionControllerImpl* controller_; + + DISALLOW_COPY_AND_ASSIGN(SelectionHandleView); +}; + +TouchSelectionControllerImpl::TouchSelectionControllerImpl( + TouchSelectionClientView* client_view) + : client_view_(client_view), + selection_handle_1_(new SelectionHandleView(this)), + selection_handle_2_(new SelectionHandleView(this)), + dragging_handle_(NULL) { +} + +TouchSelectionControllerImpl::~TouchSelectionControllerImpl() { +} + +void TouchSelectionControllerImpl::SelectionChanged(const gfx::Point& p1, + const gfx::Point& p2) { + gfx::Point screen_pos_1(p1); + View::ConvertPointToScreen(client_view_, &screen_pos_1); + gfx::Point screen_pos_2(p2); + View::ConvertPointToScreen(client_view_, &screen_pos_2); + + if (dragging_handle_) { + // We need to reposition only the selection handle that is being dragged. + // The other handle stays the same. + SelectionHandleView* fixed_handle = selection_handle_1_.get(); + if (fixed_handle == dragging_handle_) + fixed_handle = selection_handle_2_.get(); + + gfx::Point fixed_handle_pos = fixed_handle->GetScreenPosition(); + fixed_handle_pos.Offset(kSelectionHandleRadius, 0); + + if (fixed_handle_pos == screen_pos_1) + dragging_handle_->SetScreenPosition(screen_pos_2); + else + dragging_handle_->SetScreenPosition(screen_pos_1); + } else { + // Check if there is any selection at all. + if (screen_pos_1 == screen_pos_2) { + selection_handle_1_->SetVisible(false); + selection_handle_2_->SetVisible(false); + return; + } + + if (client_view_->bounds().Contains(p1)) { + selection_handle_1_->SetScreenPosition(screen_pos_1); + selection_handle_1_->SetVisible(true); + } else { + selection_handle_1_->SetVisible(false); + } + + if (client_view_->bounds().Contains(p2)) { + selection_handle_2_->SetScreenPosition(screen_pos_2); + selection_handle_2_->SetVisible(true); + } else { + selection_handle_2_->SetVisible(false); + } + } +} + +void TouchSelectionControllerImpl::ClientViewLostFocus() { + selection_handle_1_->SetVisible(false); + selection_handle_2_->SetVisible(false); +} + +void TouchSelectionControllerImpl::SelectionHandleDragged(int x) { + if (client_view_->GetWidget()) { + DCHECK(dragging_handle_); + // Find the stationary selection handle. + SelectionHandleView* fixed_handle = selection_handle_1_.get(); + if (fixed_handle == dragging_handle_) + fixed_handle = selection_handle_2_.get(); + + // Find selection end points in client_view's coordinate system. + gfx::Point p1(x + kSelectionHandleRadius, -1); + ConvertPointToClientView(dragging_handle_, &p1); + + gfx::Point p2(kSelectionHandleRadius, -1); + ConvertPointToClientView(fixed_handle, &p2); + + // Instruct client_view to select the region between p1 and p2. The position + // of |fixed_handle| is the start and that of |dragging_handle| is the end + // of selection. + client_view_->SelectRect(p2, p1); + } +} + +void TouchSelectionControllerImpl::ConvertPointToClientView( + SelectionHandleView* source, gfx::Point* point) { + View::ConvertPointToScreen(source, point); + gfx::Rect r = client_view_->GetWidget()->GetClientAreaScreenBounds(); + point->SetPoint(point->x() - r.x(), point->y() - r.y()); + View::ConvertPointFromWidget(client_view_, point); +} + +TouchSelectionController* TouchSelectionController::create( + TouchSelectionClientView* client_view) { + return new TouchSelectionControllerImpl(client_view); +} + +} // namespace views diff --git a/views/touchui/touch_selection_controller_impl.h b/views/touchui/touch_selection_controller_impl.h new file mode 100644 index 0000000..f5d83f4 --- /dev/null +++ b/views/touchui/touch_selection_controller_impl.h @@ -0,0 +1,53 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_ +#define VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_ +#pragma once + +#include "ui/gfx/point.h" +#include "views/touchui/touch_selection_controller.h" +#include "views/view.h" + +namespace views { + +// Touch specific implementation of TouchSelectionController. Responsible for +// displaying selection handles and menu elements relevant in a touch interface. +class TouchSelectionControllerImpl : public TouchSelectionController { + public: + // Use TextSelectionController::create(). + explicit TouchSelectionControllerImpl(TouchSelectionClientView* client_view); + + virtual ~TouchSelectionControllerImpl(); + + // TextSelectionController. + virtual void SelectionChanged(const gfx::Point& p1, + const gfx::Point& p2) OVERRIDE; + + virtual void ClientViewLostFocus() OVERRIDE; + + private: + class SelectionHandleView; + + // Callback to inform the client view that the selection handle has been + // dragged, hence selection may need to be updated. + void SelectionHandleDragged(int x); + + // Convenience method to convert a point from a selection handle's coordinate + // system to that of the client view. + void ConvertPointToClientView(SelectionHandleView* source, gfx::Point* point); + + TouchSelectionClientView* client_view_; + scoped_ptr<SelectionHandleView> selection_handle_1_; + scoped_ptr<SelectionHandleView> selection_handle_2_; + + // Pointer to the SelectionHandleView being dragged during a drag session. + SelectionHandleView* dragging_handle_; + + DISALLOW_COPY_AND_ASSIGN(TouchSelectionControllerImpl); +}; + +} // namespace views + +#endif // VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_ |