Implementing context menu for the new views textfield.

BUG=none
TEST=right click on the new textfield should show the context menu and all commands on the menu should work.

Review URL: http://codereview.chromium.org/6155011

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@71467 0039d316-1c4b-4281-b951-d872f2087c98
parent 45b716ba
......@@ -263,6 +263,9 @@ need to be translated for each locale.-->
<message name="IDS_APP_PASTE" desc="The text label of the Paste menu item">
&amp;Paste
</message>
<message name="IDS_APP_DELETE" desc="The text label of the Delete menu item">
&amp;Delete
</message>
<message name="IDS_APP_SELECT_ALL" desc="The text label of the Select All menu item">
Select &amp;all
</message>
......
......@@ -400,24 +400,3 @@ NativeMenuDOMUI* NativeMenuDOMUI::FindMenuAt(const gfx::Point& point) {
}
} // namespace chromeos
////////////////////////////////////////////////////////////////////////////////
// MenuWrapper, public:
namespace views {
// static
MenuWrapper* MenuWrapper::CreateWrapper(Menu2* menu) {
ui::MenuModel* model = menu->model();
if (chromeos::MenuUI::IsEnabled()) {
return new chromeos::NativeMenuDOMUI(model, true);
} else {
#if defined(TOUCH_UI)
return new NativeMenuX(menu);
#else
return new NativeMenuGtk(menu);
#endif
}
}
} // namespace views
......@@ -555,13 +555,9 @@ void NativeMenuGtk::MenuDestroyed(GtkWidget* widget, Menu2* menu2) {
////////////////////////////////////////////////////////////////////////////////
// MenuWrapper, public:
#if !defined(OS_CHROMEOS)
// static
MenuWrapper* MenuWrapper::CreateWrapper(Menu2* menu) {
return new NativeMenuGtk(menu);
}
#endif // OS_CHROMEOS
} // namespace views
......@@ -156,11 +156,9 @@ void NativeMenuX::UpdateMenuFromModel(SubmenuView* menu,
// MenuWrapper, public:
#if !defined(OS_CHROMEOS)
// static
MenuWrapper* MenuWrapper::CreateWrapper(Menu2* menu) {
return new NativeMenuX(menu);
}
#endif
} // namespace views
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// 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.
......@@ -13,11 +13,15 @@
#include "gfx/canvas.h"
#include "gfx/canvas_skia.h"
#include "gfx/insets.h"
#include "grit/app_strings.h"
#include "ui/base/clipboard/clipboard.h"
#include "views/background.h"
#include "views/border.h"
#include "views/controls/menu/menu_2.h"
#include "views/controls/textfield/textfield.h"
#include "views/controls/textfield/textfield_views_model.h"
#include "views/event.h"
#include "views/views_delegate.h"
namespace {
......@@ -62,6 +66,8 @@ NativeTextfieldViews::NativeTextfieldViews(Textfield* parent)
DCHECK_NE(parent->style(), Textfield::STYLE_MULTILINE);
// Lowercase is not supported.
DCHECK_NE(parent->style(), Textfield::STYLE_LOWERCASE);
SetContextMenuController(this);
}
NativeTextfieldViews::~NativeTextfieldViews() {
......@@ -72,10 +78,12 @@ NativeTextfieldViews::~NativeTextfieldViews() {
bool NativeTextfieldViews::OnMousePressed(const views::MouseEvent& e) {
textfield_->RequestFocus();
size_t pos = FindCursorPosition(e.location());
if (model_->MoveCursorTo(pos, false)) {
UpdateCursorBoundsAndTextOffset();
SchedulePaint();
if (e.IsLeftMouseButton()) {
size_t pos = FindCursorPosition(e.location());
if (model_->MoveCursorTo(pos, false)) {
UpdateCursorBoundsAndTextOffset();
SchedulePaint();
}
}
return true;
}
......@@ -131,6 +139,15 @@ void NativeTextfieldViews::WillLoseFocus() {
NOTREACHED();
}
/////////////////////////////////////////////////////////////////
// NativeTextfieldViews, views::ContextMenuController overrides:
void NativeTextfieldViews::ShowContextMenu(View* source,
const gfx::Point& p,
bool is_mouse_gesture) {
InitContextMenuIfRequired();
context_menu_menu_->RunContextMenuAt(p);
}
/////////////////////////////////////////////////////////////////
// NativeTextfieldViews, NativeTextifieldWrapper overrides:
......@@ -292,6 +309,65 @@ void NativeTextfieldViews::HandleWillLoseFocus() {
}
}
/////////////////////////////////////////////////////////////////
// NativeTextfieldViews, menus::SimpleMenuModel::Delegate overrides:
bool NativeTextfieldViews::IsCommandIdChecked(int command_id) const {
return true;
}
bool NativeTextfieldViews::IsCommandIdEnabled(int command_id) const {
string16 result;
switch (command_id) {
case IDS_APP_CUT:
return model_->HasSelection();
case IDS_APP_COPY:
return model_->HasSelection();
case IDS_APP_PASTE:
views::ViewsDelegate::views_delegate->GetClipboard()
->ReadText(ui::Clipboard::BUFFER_STANDARD, &result);
return !result.empty();
case IDS_APP_DELETE:
return model_->HasSelection();
case IDS_APP_SELECT_ALL:
return true;
default:
NOTREACHED();
return false;
}
}
bool NativeTextfieldViews::GetAcceleratorForCommandId(int command_id,
menus::Accelerator* accelerator) {
return false;
}
void NativeTextfieldViews::ExecuteCommand(int command_id) {
bool text_changed = false;
switch (command_id) {
case IDS_APP_CUT:
text_changed = model_->Cut();
break;
case IDS_APP_COPY:
model_->Copy();
break;
case IDS_APP_PASTE:
text_changed = model_->Paste();
break;
case IDS_APP_DELETE:
text_changed = model_->Delete();
break;
case IDS_APP_SELECT_ALL:
SelectAll();
break;
default:
NOTREACHED() << "unknown command: " << command_id;
break;
}
if (text_changed)
PropagateTextChange();
}
// static
bool NativeTextfieldViews::IsTextfieldViewsEnabled() {
#if defined(TOUCH_UI)
......@@ -522,12 +598,8 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) {
model_->Replace(print_char);
text_changed = true;
}
if (text_changed) {
textfield_->SyncText();
Textfield::Controller* controller = textfield_->GetController();
if (controller)
controller->ContentsChanged(textfield_, GetText());
}
if (text_changed)
PropagateTextChange();
if (cursor_changed) {
is_cursor_visible_ = true;
RepaintCursor();
......@@ -690,6 +762,27 @@ size_t NativeTextfieldViews::FindCursorPosition(const gfx::Point& point) const {
return left_pos;
}
void NativeTextfieldViews::PropagateTextChange() {
textfield_->SyncText();
Textfield::Controller* controller = textfield_->GetController();
if (controller)
controller->ContentsChanged(textfield_, GetText());
}
void NativeTextfieldViews::InitContextMenuIfRequired() {
if (context_menu_menu_.get())
return;
context_menu_contents_.reset(new menus::SimpleMenuModel(this));
context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT);
context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY);
context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE);
context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE);
context_menu_contents_->AddSeparator();
context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL,
IDS_APP_SELECT_ALL);
context_menu_menu_.reset(new Menu2(context_menu_contents_.get()));
}
///////////////////////////////////////////////////////////////////////////////
//
// TextifieldBorder
......
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// 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.
......@@ -6,6 +6,7 @@
#define VIEWS_CONTROLS_TEXTFIELD_NATIVE_TEXTFIELD_VIEWS_H_
#pragma once
#include "app/menus/simple_menu_model.h"
#include "base/string16.h"
#include "base/task.h"
#include "gfx/font.h"
......@@ -20,13 +21,13 @@ class Canvas;
namespace views {
class KeyEvent;
class Menu2;
class TextfieldViewsModel;
// A views/skia only implementation of NativeTextfieldWrapper.
// No platform specific code is used.
// Following features are not yet supported.
// * BIDI
// * Context Menu.
// * IME/i18n support.
// * X selection (only if we want to support).
// * STYLE_MULTILINE, STYLE_LOWERCASE text. (These are not used in
......@@ -34,7 +35,9 @@ class TextfieldViewsModel;
// * Double click to select word, and triple click to select all.
// * Undo/Redo
class NativeTextfieldViews : public views::View,
public NativeTextfieldWrapper {
public views::ContextMenuController,
public NativeTextfieldWrapper,
public menus::SimpleMenuModel::Delegate {
public:
explicit NativeTextfieldViews(Textfield* parent);
~NativeTextfieldViews();
......@@ -52,6 +55,11 @@ class NativeTextfieldViews : public views::View,
virtual void DidGainFocus();
virtual void WillLoseFocus();
// views::ContextMenuController overrides:
virtual void ShowContextMenu(View* source,
const gfx::Point& p,
bool is_mouse_gesture);
// NativeTextfieldWrapper overrides:
virtual string16 GetText() const;
virtual void UpdateText();
......@@ -79,6 +87,13 @@ class NativeTextfieldViews : public views::View,
virtual void HandleDidGainFocus();
virtual void HandleWillLoseFocus();
// menus::SimpleMenuModel::Delegate overrides
virtual bool IsCommandIdChecked(int command_id) const;
virtual bool IsCommandIdEnabled(int command_id) const;
virtual bool GetAcceleratorForCommandId(int command_id,
menus::Accelerator* accelerator);
virtual void ExecuteCommand(int command_id);
// class name of internal
static const char kViewClassName[];
......@@ -144,6 +159,13 @@ class NativeTextfieldViews : public views::View,
// Find a cusor position for given |point| in this views coordinates.
size_t FindCursorPosition(const gfx::Point& point) const;
// Utility function to inform the parent textfield (and its controller if any)
// that the text in the textfield has changed.
void PropagateTextChange();
// Utility function to create the context menu if one does not already exist.
void InitContextMenuIfRequired();
// The parent textfield, the owner of this object.
Textfield* textfield_;
......@@ -168,6 +190,10 @@ class NativeTextfieldViews : public views::View,
// A runnable method factory for callback to update the cursor.
ScopedRunnableMethodFactory<NativeTextfieldViews> cursor_timer_;
// Context menu and its content list for the textfield.
scoped_ptr<menus::SimpleMenuModel> context_menu_contents_;
scoped_ptr<Menu2> context_menu_menu_;
DISALLOW_COPY_AND_ASSIGN(NativeTextfieldViews);
};
......
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// 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 "base/auto_reset.h"
#include "base/message_loop.h"
#include "base/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/keycodes/keyboard_codes.h"
#include "views/controls/menu/menu_2.h"
#include "views/controls/textfield/native_textfield_views.h"
#include "views/controls/textfield/textfield.h"
#include "views/controls/textfield/textfield_views_model.h"
#include "views/event.h"
#include "views/focus/focus_manager.h"
#include "views/test/test_views_delegate.h"
#include "views/test/views_test_base.h"
#include "views/widget/widget.h"
#include "views/views_delegate.h"
namespace views {
......@@ -87,6 +94,11 @@ class NativeTextfieldViewsTest : public ViewsTestBase,
model_ = textfield_view_->model_.get();
}
views::Menu2* GetContextMenu() {
textfield_view_->InitContextMenuIfRequired();
return textfield_view_->context_menu_menu_.get();
}
protected:
bool SendKeyEventToTextfieldViews(ui::KeyboardCode key_code,
bool shift,
......@@ -384,4 +396,31 @@ TEST_F(NativeTextfieldViewsTest, MAYBE_FocusTraversalTest) {
EXPECT_EQ(1, GetFocusedView()->GetID());
}
void VerifyTextfieldContextMenuContents(bool textfield_has_selection,
menus::MenuModel* menu_model) {
EXPECT_TRUE(menu_model->IsEnabledAt(4 /* Separator */));
EXPECT_TRUE(menu_model->IsEnabledAt(5 /* SELECT ALL */));
EXPECT_EQ(textfield_has_selection, menu_model->IsEnabledAt(0 /* CUT */));
EXPECT_EQ(textfield_has_selection, menu_model->IsEnabledAt(1 /* COPY */));
EXPECT_EQ(textfield_has_selection, menu_model->IsEnabledAt(3 /* DELETE */));
string16 str;
views::ViewsDelegate::views_delegate->GetClipboard()
->ReadText(ui::Clipboard::BUFFER_STANDARD, &str);
EXPECT_NE(str.empty(), menu_model->IsEnabledAt(2 /* PASTE */));
}
TEST_F(NativeTextfieldViewsTest, ContextMenuDisplayTest) {
scoped_ptr<TestViewsDelegate> test_views_delegate(new TestViewsDelegate());
AutoReset<views::ViewsDelegate*> auto_reset(
&views::ViewsDelegate::views_delegate, test_views_delegate.get());
views::ViewsDelegate::views_delegate = test_views_delegate.get();
InitTextfield(Textfield::STYLE_DEFAULT);
textfield_->SetText(ASCIIToUTF16("hello world"));
EXPECT_TRUE(GetContextMenu());
VerifyTextfieldContextMenuContents(false, GetContextMenu()->model());
textfield_->SelectAll();
VerifyTextfieldContextMenuContents(true, GetContextMenu()->model());
}
} // namespace views
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// 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.
......@@ -137,12 +137,12 @@ class TextfieldViewsModel {
// if text has changed after pasting.
bool Paste();
private:
friend class NativeTextfieldViews;
// Tells if any text is selected.
bool HasSelection() const;
private:
friend class NativeTextfieldViews;
// Deletes the selected text.
void DeleteSelection();
......
......@@ -54,19 +54,6 @@ class ContainerView : public View {
} // namespace
namespace views {
// OS_CHROMEOS requires a MenuWrapper::CreateWrapper implementation.
// TODO(oshima): Fix chromium-os:7409 so that this isn't required.
#if defined(OS_CHROMEOS)
// static
MenuWrapper* MenuWrapper::CreateWrapper(Menu2* menu) {
return new NativeMenuGtk(menu);
}
#endif // OS_CHROMEOS
} // namespace views
namespace examples {
ExampleBase::ExampleBase(ExamplesMain* main)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment