summaryrefslogtreecommitdiffstats
path: root/views/touchui
diff options
context:
space:
mode:
authorvarunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-26 20:11:53 +0000
committervarunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-26 20:11:53 +0000
commit4723a449a5e77386344de98ce09667c5acab6aa7 (patch)
tree7e8fc621cb231d424eb94c67b7fa39c9d23175c3 /views/touchui
parentde09c7d0478d7a82785aa5080a9499ee8b36b1d8 (diff)
downloadchromium_src-4723a449a5e77386344de98ce09667c5acab6aa7.zip
chromium_src-4723a449a5e77386344de98ce09667c5acab6aa7.tar.gz
chromium_src-4723a449a5e77386344de98ce09667c5acab6aa7.tar.bz2
Implement touch selection menu.
BUG=none TEST=none Review URL: http://codereview.chromium.org/7740024 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@98473 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views/touchui')
-rw-r--r--views/touchui/touch_selection_controller_impl.cc183
-rw-r--r--views/touchui/touch_selection_controller_impl.h23
2 files changed, 202 insertions, 4 deletions
diff --git a/views/touchui/touch_selection_controller_impl.cc b/views/touchui/touch_selection_controller_impl.cc
index 10bf572..0de16ce 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();
}
diff --git a/views/touchui/touch_selection_controller_impl.h b/views/touchui/touch_selection_controller_impl.h
index 6cb7816..480485d 100644
--- a/views/touchui/touch_selection_controller_impl.h
+++ b/views/touchui/touch_selection_controller_impl.h
@@ -6,6 +6,7 @@
#define VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_
#pragma once
+#include "base/timer.h"
#include "ui/gfx/point.h"
#include "views/touchui/touch_selection_controller.h"
#include "views/view.h"
@@ -32,6 +33,7 @@ class VIEWS_EXPORT TouchSelectionControllerImpl
private:
friend class TouchSelectionControllerImplTest;
class SelectionHandleView;
+ class TouchContextMenuView;
// Callback to inform the client view that the selection handle has been
// dragged, hence selection may need to be updated.
@@ -41,6 +43,21 @@ class VIEWS_EXPORT TouchSelectionControllerImpl
// system to that of the client view.
void ConvertPointToClientView(SelectionHandleView* source, gfx::Point* point);
+ // Checks if the client view supports a context menu command.
+ bool IsCommandIdEnabled(int command_id) const;
+
+ // Sends a context menu command to the client view.
+ void ExecuteCommand(int command_id);
+
+ // Time to show context menu.
+ void ContextMenuTimerFired();
+
+ // Convenience method to update the position/visibility of the context menu.
+ void UpdateContextMenu(const gfx::Point& p1, const gfx::Point& p2);
+
+ // Convenience method for hiding context menu.
+ void HideContextMenu();
+
// Convenience methods for testing.
gfx::Point GetSelectionHandle1Position();
gfx::Point GetSelectionHandle2Position();
@@ -50,6 +67,12 @@ class VIEWS_EXPORT TouchSelectionControllerImpl
TouchSelectionClientView* client_view_;
scoped_ptr<SelectionHandleView> selection_handle_1_;
scoped_ptr<SelectionHandleView> selection_handle_2_;
+ scoped_ptr<TouchContextMenuView> context_menu_;
+
+ // Timer to trigger |context_menu| (|context_menu| is not shown if the
+ // selection handles are being updated. It appears only when the handles are
+ // stationary for a certain amount of time).
+ base::OneShotTimer<TouchSelectionControllerImpl> context_menu_timer_;
// Pointer to the SelectionHandleView being dragged during a drag session.
SelectionHandleView* dragging_handle_;