| Index: views/touchui/touch_selection_controller_impl.cc
|
| diff --git a/views/touchui/touch_selection_controller_impl.cc b/views/touchui/touch_selection_controller_impl.cc
|
| index 10bf572820a0f7849de07cf8cce17b290d2123c6..0de16ce8dec7d1187b42f116d9791469943a63dc 100644
|
| --- a/views/touchui/touch_selection_controller_impl.cc
|
| +++ b/views/touchui/touch_selection_controller_impl.cc
|
| @@ -4,15 +4,23 @@
|
|
|
| #include "views/touchui/touch_selection_controller_impl.h"
|
|
|
| +#include "base/time.h"
|
| +#include "grit/ui_strings.h"
|
| +#include "ui/base/l10n/l10n_util.h"
|
| +#include "ui/base/resource/resource_bundle.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/screen.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"
|
| +#include "views/controls/button/button.h"
|
| +#include "views/controls/button/text_button.h"
|
| +#include "views/controls/label.h"
|
| +#include "views/layout/box_layout.h"
|
| +#include "views/widget/widget.h"
|
|
|
| namespace {
|
|
|
| @@ -23,6 +31,17 @@ const int kSelectionHandleAlpha = 0x7F;
|
| const SkColor kSelectionHandleColor =
|
| SkColorSetA(SK_ColorBLUE, kSelectionHandleAlpha);
|
|
|
| +const int kContextMenuCommands[] = {IDS_APP_CUT,
|
| + IDS_APP_COPY,
|
| +// TODO(varunjain): PASTE is acting funny due to some gtk clipboard issue.
|
| +// Uncomment the following when that is fixed.
|
| +// IDS_APP_PASTE,
|
| + IDS_APP_DELETE,
|
| + IDS_APP_SELECT_ALL};
|
| +const int kContextMenuPadding = 2;
|
| +const int kContextMenuTimoutMs = 1000;
|
| +const int kContextMenuVerticalOffset = 10;
|
| +
|
| // Convenience struct to represent a circle shape.
|
| struct Circle {
|
| int radius;
|
| @@ -31,7 +50,7 @@ struct Circle {
|
| };
|
|
|
| // Creates a widget to host SelectionHandleView.
|
| -views::Widget* CreateSelectionHandleWidget() {
|
| +views::Widget* CreateTouchSelectionPopupWidget() {
|
| views::Widget* widget = new views::Widget;
|
| views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
|
| params.can_activate = false;
|
| @@ -68,7 +87,7 @@ class TouchSelectionControllerImpl::SelectionHandleView : public View {
|
| public:
|
| SelectionHandleView(TouchSelectionControllerImpl* controller)
|
| : controller_(controller) {
|
| - widget_.reset(CreateSelectionHandleWidget());
|
| + widget_.reset(CreateTouchSelectionPopupWidget());
|
| widget_->SetContentsView(this);
|
| widget_->SetAlwaysOnTop(true);
|
|
|
| @@ -141,11 +160,103 @@ class TouchSelectionControllerImpl::SelectionHandleView : public View {
|
| DISALLOW_COPY_AND_ASSIGN(SelectionHandleView);
|
| };
|
|
|
| +// A View that displays the touch context menu.
|
| +class TouchSelectionControllerImpl::TouchContextMenuView
|
| + : public ButtonListener,
|
| + public View {
|
| + public:
|
| + TouchContextMenuView(TouchSelectionControllerImpl* controller)
|
| + : controller_(controller) {
|
| + widget_.reset(CreateTouchSelectionPopupWidget());
|
| + widget_->SetContentsView(this);
|
| + widget_->SetAlwaysOnTop(true);
|
| +
|
| + // We are owned by the TouchSelectionController.
|
| + set_parent_owned(false);
|
| + SetLayoutManager(new BoxLayout(BoxLayout::kHorizontal, kContextMenuPadding,
|
| + kContextMenuPadding, kContextMenuPadding));
|
| + }
|
| +
|
| + virtual ~TouchContextMenuView() {
|
| + widget_->Close();
|
| + }
|
| +
|
| + 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);
|
| + }
|
| +
|
| + void SetScreenPosition(const gfx::Point& position) {
|
| + RefreshButtonsAndSetWidgetPosition(position);
|
| + }
|
| +
|
| + gfx::Point GetScreenPosition() {
|
| + return widget_->GetClientAreaScreenBounds().origin();
|
| + }
|
| +
|
| + // ButtonListener
|
| + virtual void ButtonPressed(Button* sender, const views::Event& event) {
|
| + controller_->ExecuteCommand(sender->tag());
|
| + }
|
| +
|
| + private:
|
| + // Queries the client view for what elements to show in the menu and sizes
|
| + // the menu appropriately.
|
| + void RefreshButtonsAndSetWidgetPosition(const gfx::Point& position) {
|
| + RemoveAllChildViews(true);
|
| + int total_width = 0;
|
| + int height = 0;
|
| + for (size_t i = 0; i < arraysize(kContextMenuCommands); i++) {
|
| + int command_id = kContextMenuCommands[i];
|
| + if (controller_->IsCommandIdEnabled(command_id)) {
|
| + TextButton* button = new TextButton(this,
|
| + UTF16ToWide(l10n_util::GetStringUTF16(command_id)));
|
| + button->set_focusable(true);
|
| + button->set_request_focus_on_press(false);
|
| + button->set_prefix_type(TextButton::PREFIX_HIDE);
|
| + button->SetEnabledColor(SK_ColorWHITE);
|
| + button->SetHoverColor(SK_ColorWHITE);
|
| + button->set_background(
|
| + Background::CreateSolidBackground(SK_ColorBLACK));
|
| + button->set_alignment(TextButton::ALIGN_CENTER);
|
| + button->SetFont(ui::ResourceBundle::GetSharedInstance().GetFont(
|
| + ui::ResourceBundle::LargeFont));
|
| + button->set_tag(command_id);
|
| + AddChildView(button);
|
| + gfx::Size button_size = button->GetPreferredSize();
|
| + total_width += button_size.width() + kContextMenuPadding;
|
| + if (height < button_size.height())
|
| + height = button_size.height();
|
| + }
|
| + }
|
| + gfx::Rect widget_bounds(position.x() - total_width / 2,
|
| + position.y() - height,
|
| + total_width,
|
| + height);
|
| + gfx::Rect monitor_bounds =
|
| + gfx::Screen::GetMonitorAreaNearestPoint(position);
|
| + widget_->SetBounds(widget_bounds.AdjustToFit(monitor_bounds));
|
| + Layout();
|
| + }
|
| +
|
| + scoped_ptr<Widget> widget_;
|
| + TouchSelectionControllerImpl* controller_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(TouchContextMenuView);
|
| +};
|
| +
|
| TouchSelectionControllerImpl::TouchSelectionControllerImpl(
|
| TouchSelectionClientView* client_view)
|
| : client_view_(client_view),
|
| selection_handle_1_(new SelectionHandleView(this)),
|
| selection_handle_2_(new SelectionHandleView(this)),
|
| + context_menu_(new TouchContextMenuView(this)),
|
| dragging_handle_(NULL) {
|
| }
|
|
|
| @@ -166,6 +277,8 @@ void TouchSelectionControllerImpl::SelectionChanged(const gfx::Point& p1,
|
| // the start.
|
| dragging_handle_->SetScreenPosition(screen_pos_2);
|
| } else {
|
| + UpdateContextMenu(p1, p2);
|
| +
|
| // Check if there is any selection at all.
|
| if (screen_pos_1 == screen_pos_2) {
|
| selection_handle_1_->SetVisible(false);
|
| @@ -192,10 +305,18 @@ void TouchSelectionControllerImpl::SelectionChanged(const gfx::Point& p1,
|
| void TouchSelectionControllerImpl::ClientViewLostFocus() {
|
| selection_handle_1_->SetVisible(false);
|
| selection_handle_2_->SetVisible(false);
|
| + HideContextMenu();
|
| }
|
|
|
| void TouchSelectionControllerImpl::SelectionHandleDragged(
|
| const gfx::Point& drag_pos) {
|
| + // We do not want to show the context menu while dragging.
|
| + HideContextMenu();
|
| + context_menu_timer_.Start(
|
| + base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs),
|
| + this,
|
| + &TouchSelectionControllerImpl::ContextMenuTimerFired);
|
| +
|
| if (client_view_->GetWidget()) {
|
| DCHECK(dragging_handle_);
|
| // Find the stationary selection handle.
|
| @@ -225,6 +346,60 @@ void TouchSelectionControllerImpl::ConvertPointToClientView(
|
| View::ConvertPointFromWidget(client_view_, point);
|
| }
|
|
|
| +bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const {
|
| + return client_view_->IsCommandIdEnabled(command_id);
|
| +}
|
| +
|
| +void TouchSelectionControllerImpl::ExecuteCommand(int command_id) {
|
| + HideContextMenu();
|
| + client_view_->ExecuteCommand(command_id);
|
| +}
|
| +
|
| +void TouchSelectionControllerImpl::ContextMenuTimerFired() {
|
| + // Get selection end points in client_view's space.
|
| + gfx::Point p1(kSelectionHandleRadius, 0);
|
| + ConvertPointToClientView(selection_handle_1_.get(), &p1);
|
| + gfx::Point p2(kSelectionHandleRadius, 0);
|
| + ConvertPointToClientView(selection_handle_2_.get(), &p2);
|
| +
|
| + // if selection is completely inside the view, we display the context menu
|
| + // in the middle of the end points on the top. Else, we show the menu on the
|
| + // top border of the view in the center.
|
| + gfx::Point menu_pos;
|
| + if (client_view_->bounds().Contains(p1) &&
|
| + client_view_->bounds().Contains(p2)) {
|
| + menu_pos.set_x((p1.x() + p2.x()) / 2);
|
| + menu_pos.set_y(std::min(p1.y(), p2.y()) - kContextMenuVerticalOffset);
|
| + } else {
|
| + menu_pos.set_x(client_view_->x() + client_view_->width() / 2);
|
| + menu_pos.set_y(client_view_->y());
|
| + }
|
| +
|
| + View::ConvertPointToScreen(client_view_, &menu_pos);
|
| +
|
| + context_menu_->SetScreenPosition(menu_pos);
|
| + context_menu_->SetVisible(true);
|
| +}
|
| +
|
| +void TouchSelectionControllerImpl::UpdateContextMenu(const gfx::Point& p1,
|
| + const gfx::Point& p2) {
|
| + // Hide context menu to be shown when the timer fires.
|
| + HideContextMenu();
|
| +
|
| + // If there is selection, we restart the context menu timer.
|
| + if (p1 != p2) {
|
| + context_menu_timer_.Start(
|
| + base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs),
|
| + this,
|
| + &TouchSelectionControllerImpl::ContextMenuTimerFired);
|
| + }
|
| +}
|
| +
|
| +void TouchSelectionControllerImpl::HideContextMenu() {
|
| + context_menu_->SetVisible(false);
|
| + context_menu_timer_.Stop();
|
| +}
|
| +
|
| gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() {
|
| return selection_handle_1_->GetScreenPosition();
|
| }
|
|
|