diff options
author | msw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-23 01:40:42 +0000 |
---|---|---|
committer | msw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-23 01:40:42 +0000 |
commit | a44af4634f25e7e74e13b34c793361849a55b741 (patch) | |
tree | 8b05977f9cbb408b543130e775e45d94aaa1e519 | |
parent | 80f360a3d6b499599615ca70701363f7ddf397a4 (diff) | |
download | chromium_src-a44af4634f25e7e74e13b34c793361849a55b741.zip chromium_src-a44af4634f25e7e74e13b34c793361849a55b741.tar.gz chromium_src-a44af4634f25e7e74e13b34c793361849a55b741.tar.bz2 |
Views Textfield: copy-on-select and paste-on-middle-click.
Add Textfield::PasteSelectionClipboard helper function.
(used to paste on middle click, update cursor/selection)
Add file-local UpdateSelectionClipboard helper function.
(used to write non-empty text to the selection clipboard)
Call UpdateAfterChange consistently after changes.
(not just OnCaretBoundsChanged, SchedulePaint, etc.)
Cleanup ExecuteCommand, OnMousePressed/Dragged, etc.
Add TextfieldTest.SelectionClipboard unit tests.
Make Get/SetClipboardText helpers file-local.
BUG=319011
R=erg@chromium.org,oshima@chromium.org
TEST=Copy-on-select and paste-on-middle-click work for Linux Aura omnibox and browser textfields (find-in-page, bookmark bubble).
NOTRY=true
Review URL: https://codereview.chromium.org/73403002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@246478 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | ui/views/controls/textfield/textfield.cc | 172 | ||||
-rw-r--r-- | ui/views/controls/textfield/textfield.h | 18 | ||||
-rw-r--r-- | ui/views/controls/textfield/textfield_unittest.cc | 200 |
3 files changed, 254 insertions, 136 deletions
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc index 88d4c1e..5c6c801 100644 --- a/ui/views/controls/textfield/textfield.cc +++ b/ui/views/controls/textfield/textfield.cc @@ -9,6 +9,7 @@ #include "base/debug/trace_event.h" #include "grit/ui_strings.h" #include "ui/base/accessibility/accessible_view_state.h" +#include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/drag_utils.h" #include "ui/base/resource/resource_bundle.h" @@ -150,8 +151,8 @@ base::i18n::TextDirection Textfield::GetTextDirection() const { void Textfield::SelectAll(bool reversed) { model_->SelectAll(reversed); - OnCaretBoundsChanged(); - SchedulePaint(); + UpdateSelectionClipboard(); + UpdateAfterChange(false, true); } base::string16 Textfield::GetSelectedText() const { @@ -160,8 +161,7 @@ base::string16 Textfield::GetSelectedText() const { void Textfield::ClearSelection() { model_->ClearSelection(); - OnCaretBoundsChanged(); - SchedulePaint(); + UpdateAfterChange(false, true); } bool Textfield::HasSelection() const { @@ -244,10 +244,7 @@ const gfx::Range& Textfield::GetSelectedRange() const { void Textfield::SelectRange(const gfx::Range& range) { model_->SelectRange(range); - OnCaretBoundsChanged(); - SchedulePaint(); - NotifyAccessibilityEvent( - ui::AccessibilityTypes::EVENT_SELECTION_CHANGED, true); + UpdateAfterChange(false, true); } const gfx::SelectionModel& Textfield::GetSelectionModel() const { @@ -256,8 +253,7 @@ const gfx::SelectionModel& Textfield::GetSelectionModel() const { void Textfield::SelectSelectionModel(const gfx::SelectionModel& sel) { model_->SelectSelectionModel(sel); - OnCaretBoundsChanged(); - SchedulePaint(); + UpdateAfterChange(false, true); } size_t Textfield::GetCursorPosition() const { @@ -373,6 +369,7 @@ bool Textfield::OnKeyPressed(const ui::KeyEvent& event) { case ui::VKEY_A: if (control && !alt) { model_->SelectAll(false); + UpdateSelectionClipboard(); cursor_changed = true; } break; @@ -400,6 +397,7 @@ bool Textfield::OnKeyPressed(const ui::KeyEvent& event) { control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK, (key_code == ui::VKEY_RIGHT) ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT, shift); + UpdateSelectionClipboard(); cursor_changed = render_text->selection() != selection_range; break; } @@ -410,6 +408,7 @@ bool Textfield::OnKeyPressed(const ui::KeyEvent& event) { model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, shift); else model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, shift); + UpdateSelectionClipboard(); cursor_changed = true; break; case ui::VKEY_BACK: @@ -463,7 +462,6 @@ bool Textfield::OnKeyPressed(const ui::KeyEvent& event) { } bool Textfield::OnMousePressed(const ui::MouseEvent& event) { - OnBeforeUserAction(); TrackMouseClicks(event); if (!controller_ || !controller_->HandleMouseEvent(this, event)) { @@ -473,36 +471,46 @@ bool Textfield::OnMousePressed(const ui::MouseEvent& event) { } if (event.IsOnlyLeftMouseButton()) { + OnBeforeUserAction(); initiating_drag_ = false; - bool can_drag = true; - switch (aggregated_clicks_) { case 0: - if (can_drag && - GetRenderText()->IsPointInSelection(event.location())) { + if (GetRenderText()->IsPointInSelection(event.location())) initiating_drag_ = true; - } else { + else MoveCursorTo(event.location(), event.IsShiftDown()); - } break; case 1: - MoveCursorTo(event.location(), false); + model_->MoveCursorTo(event.location(), false); model_->SelectWord(); + UpdateAfterChange(false, true); double_click_word_ = GetRenderText()->selection(); - OnCaretBoundsChanged(); break; case 2: - model_->SelectAll(false); - OnCaretBoundsChanged(); + SelectAll(false); break; default: NOTREACHED(); } + OnAfterUserAction(); } - SchedulePaint(); + +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + if (event.IsOnlyMiddleMouseButton()) { + if (GetRenderText()->IsPointInSelection(event.location())) { + OnBeforeUserAction(); + ClearSelection(); + ui::ScopedClipboardWriter( + ui::Clipboard::GetForCurrentThread(), + ui::CLIPBOARD_TYPE_SELECTION).WriteText(base::string16()); + OnAfterUserAction(); + } else if(!read_only()) { + PasteSelectionClipboard(event); + } + } +#endif } - OnAfterUserAction(); touch_selection_controller_.reset(); return true; } @@ -515,34 +523,33 @@ bool Textfield::OnMouseDragged(const ui::MouseEvent& event) { return true; } - if (!event.IsOnlyRightMouseButton()) { - OnBeforeUserAction(); - MoveCursorTo(event.location(), true); - if (aggregated_clicks_ == 1) { - model_->SelectWord(); - // Expand the selection so the initially selected word remains selected. - gfx::Range selection = GetRenderText()->selection(); - const size_t min = std::min(selection.GetMin(), - double_click_word_.GetMin()); - const size_t max = std::max(selection.GetMax(), - double_click_word_.GetMax()); - const bool reversed = selection.is_reversed(); - selection.set_start(reversed ? max : min); - selection.set_end(reversed ? min : max); - model_->SelectRange(selection); - } - SchedulePaint(); - OnAfterUserAction(); + OnBeforeUserAction(); + model_->MoveCursorTo(event.location(), true); + if (aggregated_clicks_ == 1) { + model_->SelectWord(); + // Expand the selection so the initially selected word remains selected. + gfx::Range selection = GetRenderText()->selection(); + const size_t min = std::min(selection.GetMin(), + double_click_word_.GetMin()); + const size_t max = std::max(selection.GetMax(), + double_click_word_.GetMax()); + const bool reversed = selection.is_reversed(); + selection.set_start(reversed ? max : min); + selection.set_end(reversed ? min : max); + model_->SelectRange(selection); } + UpdateAfterChange(false, true); + OnAfterUserAction(); return true; } void Textfield::OnMouseReleased(const ui::MouseEvent& event) { OnBeforeUserAction(); // Cancel suspected drag initiations, the user was clicking in the selection. - if (initiating_drag_ && MoveCursorTo(event.location(), false)) - SchedulePaint(); + if (initiating_drag_) + MoveCursorTo(event.location(), false); initiating_drag_ = false; + UpdateSelectionClipboard(); OnAfterUserAction(); } @@ -644,16 +651,14 @@ void Textfield::OnGestureEvent(ui::GestureEvent* event) { // We don't deselect if the point is in the selection // because TAP_DOWN may turn into a LONG_PRESS. - if (!GetRenderText()->IsPointInSelection(event->location()) && - MoveCursorTo(event->location(), false)) - SchedulePaint(); + if (!GetRenderText()->IsPointInSelection(event->location())) + MoveCursorTo(event->location(), false); OnAfterUserAction(); event->SetHandled(); break; case ui::ET_GESTURE_SCROLL_UPDATE: OnBeforeUserAction(); - if (MoveCursorTo(event->location(), true)) - SchedulePaint(); + MoveCursorTo(event->location(), true); OnAfterUserAction(); event->SetHandled(); break; @@ -689,8 +694,7 @@ void Textfield::OnGestureEvent(ui::GestureEvent* event) { model_->SelectWord(); touch_selection_controller_.reset( ui::TouchSelectionController::create(this)); - OnCaretBoundsChanged(); - SchedulePaint(); + UpdateAfterChange(false, true); OnAfterUserAction(); if (touch_selection_controller_) event->SetHandled(); @@ -899,9 +903,7 @@ void Textfield::SelectRect(const gfx::Point& start, const gfx::Point& end) { end_caret.caret_affinity()); OnBeforeUserAction(); - model_->SelectSelectionModel(selection); - OnCaretBoundsChanged(); - SchedulePaint(); + SelectSelectionModel(selection); OnAfterUserAction(); } @@ -985,46 +987,32 @@ void Textfield::ExecuteCommand(int command_id, int event_flags) { return; bool text_changed = false; + OnBeforeUserAction(); switch (command_id) { case IDS_APP_UNDO: - OnBeforeUserAction(); text_changed = model_->Undo(); - UpdateAfterChange(text_changed, text_changed); - OnAfterUserAction(); break; case IDS_APP_CUT: - OnBeforeUserAction(); text_changed = Cut(); - UpdateAfterChange(text_changed, text_changed); - OnAfterUserAction(); break; case IDS_APP_COPY: - OnBeforeUserAction(); Copy(); - OnAfterUserAction(); break; case IDS_APP_PASTE: - OnBeforeUserAction(); text_changed = Paste(); - UpdateAfterChange(text_changed, text_changed); - OnAfterUserAction(); break; case IDS_APP_DELETE: - OnBeforeUserAction(); text_changed = model_->Delete(); - UpdateAfterChange(text_changed, text_changed); - OnAfterUserAction(); break; case IDS_APP_SELECT_ALL: - OnBeforeUserAction(); SelectAll(false); - UpdateAfterChange(false, true); - OnAfterUserAction(); break; default: NOTREACHED(); break; } + UpdateAfterChange(text_changed, text_changed); + OnAfterUserAction(); } //////////////////////////////////////////////////////////////////////////////// @@ -1200,7 +1188,6 @@ bool Textfield::GetSelectionRange(gfx::Range* range) const { bool Textfield::SetSelectionRange(const gfx::Range& range) { if (!ImeEditingAllowed() || !range.IsValid()) return false; - OnBeforeUserAction(); SelectRange(range); OnAfterUserAction(); @@ -1364,11 +1351,9 @@ void Textfield::PaintTextAndCursor(gfx::Canvas* canvas) { canvas->Restore(); } -bool Textfield::MoveCursorTo(const gfx::Point& point, bool select) { - if (!model_->MoveCursorTo(point, select)) - return false; - OnCaretBoundsChanged(); - return true; +void Textfield::MoveCursorTo(const gfx::Point& point, bool select) { + if (model_->MoveCursorTo(point, select)) + UpdateAfterChange(false, true); } void Textfield::OnCaretBoundsChanged() { @@ -1477,4 +1462,39 @@ void Textfield::CreateTouchSelectionControllerAndNotifyIt() { touch_selection_controller_->SelectionChanged(); } +void Textfield::UpdateSelectionClipboard() const { +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + if (HasSelection()) { + ui::ScopedClipboardWriter( + ui::Clipboard::GetForCurrentThread(), + ui::CLIPBOARD_TYPE_SELECTION).WriteText(GetSelectedText()); + } +#endif +} + +void Textfield::PasteSelectionClipboard(const ui::MouseEvent& event) { + DCHECK(event.IsOnlyMiddleMouseButton()); + DCHECK(!read_only()); + base::string16 selection_clipboard_text; + ui::Clipboard::GetForCurrentThread()->ReadText( + ui::CLIPBOARD_TYPE_SELECTION, &selection_clipboard_text); + if (!selection_clipboard_text.empty()) { + OnBeforeUserAction(); + gfx::Range range = GetSelectionModel().selection(); + gfx::LogicalCursorDirection affinity = GetSelectionModel().caret_affinity(); + const gfx::SelectionModel mouse = + GetRenderText()->FindCursorPosition(event.location()); + model_->MoveCursorTo(mouse); + model_->InsertText(selection_clipboard_text); + // Update the new selection range as needed. + if (range.GetMin() >= mouse.caret_pos()) { + const size_t length = selection_clipboard_text.length(); + range = gfx::Range(range.start() + length, range.end() + length); + } + model_->MoveCursorTo(gfx::SelectionModel(range, affinity)); + UpdateAfterChange(true, true); + OnAfterUserAction(); + } +} + } // namespace views diff --git a/ui/views/controls/textfield/textfield.h b/ui/views/controls/textfield/textfield.h index 94d98ae..7596126 100644 --- a/ui/views/controls/textfield/textfield.h +++ b/ui/views/controls/textfield/textfield.h @@ -310,9 +310,9 @@ class VIEWS_EXPORT Textfield : public View, void PaintTextAndCursor(gfx::Canvas* canvas); // Helper function to call MoveCursorTo on the TextfieldModel. - bool MoveCursorTo(const gfx::Point& point, bool select); + void MoveCursorTo(const gfx::Point& point, bool select); - // Convenience method to call InputMethod::OnCaretBoundsChanged(); + // Convenience method to notify the InputMethod and TouchSelectionController. void OnCaretBoundsChanged(); // Convenience method to call TextfieldController::OnBeforeUserAction(); @@ -346,6 +346,12 @@ class VIEWS_EXPORT Textfield : public View, void CreateTouchSelectionControllerAndNotifyIt(); + // Updates the selection clipboard to any non-empty text selection. + void UpdateSelectionClipboard() const; + + // Pastes the selection clipboard for the specified mouse event. + void PasteSelectionClipboard(const ui::MouseEvent& event); + // The text model. scoped_ptr<TextfieldModel> model_; @@ -385,8 +391,9 @@ class VIEWS_EXPORT Textfield : public View, // The input type of this text field. ui::TextInputType text_input_type_; - // The duration to reveal the last typed char for password textfields. + // The duration and timer to reveal the last typed password character. base::TimeDelta password_reveal_duration_; + base::OneShotTimer<Textfield> password_reveal_timer_; // True if InputMethod::CancelComposition() should not be called. bool skip_input_method_cancel_composition_; @@ -410,11 +417,6 @@ class VIEWS_EXPORT Textfield : public View, scoped_ptr<ui::TouchSelectionController> touch_selection_controller_; - // A timer to control the duration of showing the last typed char in - // password text. When the timer is running, the last typed char is shown - // and when the time expires, the last typed char is password. - base::OneShotTimer<Textfield> password_reveal_timer_; - // Context menu related members. scoped_ptr<ui::SimpleMenuModel> context_menu_contents_; scoped_ptr<views::MenuRunner> context_menu_runner_; diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc index 4ab7635..503c656 100644 --- a/ui/views/controls/textfield/textfield_unittest.cc +++ b/ui/views/controls/textfield/textfield_unittest.cc @@ -109,6 +109,17 @@ class GestureEventForTest : public ui::GestureEvent { DISALLOW_COPY_AND_ASSIGN(GestureEventForTest); }; +base::string16 GetClipboardText(ui::ClipboardType type) { + base::string16 text; + ui::Clipboard::GetForCurrentThread()->ReadText(type, &text); + return text; +} + +void SetClipboardText(ui::ClipboardType type, const std::string& text) { + ui::ScopedClipboardWriter(ui::Clipboard::GetForCurrentThread(), type) + .WriteText(ASCIIToUTF16(text)); +} + } // namespace namespace views { @@ -237,20 +248,6 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { } } - base::string16 GetClipboardText() const { - base::string16 text; - ui::Clipboard::GetForCurrentThread()-> - ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &text); - return text; - } - - void SetClipboardText(const std::string& text) { - ui::ScopedClipboardWriter clipboard_writer( - ui::Clipboard::GetForCurrentThread(), - ui::CLIPBOARD_TYPE_COPY_PASTE); - clipboard_writer.WriteText(ASCIIToUTF16(text)); - } - View* GetFocusedView() { return widget_->GetFocusManager()->GetFocusedView(); } @@ -277,7 +274,7 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { // Mouse click on the point whose x-axis is |bound|'s x plus |x_offset| and // y-axis is in the middle of |bound|'s vertical range. void MouseClick(const gfx::Rect bound, int x_offset) { - gfx::Point point(bound.x() + x_offset, bound.y() + bound.height() / 2); + gfx::Point point(bound.x() + x_offset, bound.y() + bound.height() / 2); ui::MouseEvent click(ui::ET_MOUSE_PRESSED, point, point, ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); textfield_->OnMousePressed(click); @@ -298,11 +295,6 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { textfield_->OnMouseReleased(release); } - // Wrap for visibility in test classes. - ui::TextInputType GetTextInputType() { - return textfield_->GetTextInputType(); - } - void VerifyTextfieldContextMenuContents(bool textfield_has_selection, bool can_undo, ui::MenuModel* menu) { @@ -310,7 +302,8 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { EXPECT_TRUE(menu->IsEnabledAt(1 /* Separator */)); EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(2 /* CUT */)); EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(3 /* COPY */)); - EXPECT_NE(GetClipboardText().empty(), menu->IsEnabledAt(4 /* PASTE */)); + EXPECT_NE(GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE).empty(), + menu->IsEnabledAt(4 /* PASTE */)); EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(5 /* DELETE */)); EXPECT_TRUE(menu->IsEnabledAt(6 /* Separator */)); EXPECT_TRUE(menu->IsEnabledAt(7 /* SELECT ALL */)); @@ -465,7 +458,7 @@ TEST_F(TextfieldTest, InsertionDeletionTest) { TEST_F(TextfieldTest, PasswordTest) { InitTextfield(); textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); - EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, GetTextInputType()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, textfield_->GetTextInputType()); EXPECT_TRUE(textfield_->enabled()); EXPECT_TRUE(textfield_->IsFocusable()); @@ -475,7 +468,7 @@ TEST_F(TextfieldTest, PasswordTest) { EXPECT_STR_EQ("password", textfield_->text()); EXPECT_TRUE(last_contents_.empty()); model_->SelectAll(false); - SetClipboardText("foo"); + SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "foo"); // Cut and copy should be disabled. EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_CUT)); @@ -485,7 +478,7 @@ TEST_F(TextfieldTest, PasswordTest) { textfield_->ExecuteCommand(IDS_APP_COPY, 0); SendKeyEvent(ui::VKEY_C, false, true); SendKeyEvent(ui::VKEY_INSERT, false, true); - EXPECT_STR_EQ("foo", base::string16(GetClipboardText())); + EXPECT_STR_EQ("foo", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("password", textfield_->text()); // [Shift]+[Delete] should just delete without copying text to the clipboard. textfield_->SelectAll(false); @@ -496,7 +489,7 @@ TEST_F(TextfieldTest, PasswordTest) { textfield_->ExecuteCommand(IDS_APP_PASTE, 0); SendKeyEvent(ui::VKEY_V, false, true); SendKeyEvent(ui::VKEY_INSERT, true, false); - EXPECT_STR_EQ("foo", base::string16(GetClipboardText())); + EXPECT_STR_EQ("foo", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("foofoofoo", textfield_->text()); } @@ -504,27 +497,27 @@ TEST_F(TextfieldTest, TextInputType) { InitTextfield(); // Defaults to TEXT - EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, GetTextInputType()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, textfield_->GetTextInputType()); // And can be set. textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_URL); - EXPECT_EQ(ui::TEXT_INPUT_TYPE_URL, GetTextInputType()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_URL, textfield_->GetTextInputType()); textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); - EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, GetTextInputType()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, textfield_->GetTextInputType()); // Readonly textfields have type NONE textfield_->SetReadOnly(true); - EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, GetTextInputType()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, textfield_->GetTextInputType()); textfield_->SetReadOnly(false); - EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, GetTextInputType()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, textfield_->GetTextInputType()); // As do disabled textfields textfield_->SetEnabled(false); - EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, GetTextInputType()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, textfield_->GetTextInputType()); textfield_->SetEnabled(true); - EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, GetTextInputType()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, textfield_->GetTextInputType()); } TEST_F(TextfieldTest, OnKeyPressReturnValueTest) { @@ -667,7 +660,7 @@ TEST_F(TextfieldTest, FocusTraversalTest) { textfield_->RequestFocus(); EXPECT_EQ(1, GetFocusedView()->id()); - // Test if clicking on textfield view sets the focus to textfield_. + // Test if clicking on textfield view sets the focus. widget_->GetFocusManager()->AdvanceFocus(true); EXPECT_EQ(3, GetFocusedView()->id()); ui::MouseEvent click(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), @@ -695,7 +688,7 @@ TEST_F(TextfieldTest, ContextMenuDisplayTest) { VerifyTextfieldContextMenuContents(true, true, GetContextMenuModel()); // Exercise the "paste enabled?" check in the verifier. - SetClipboardText("Test"); + SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test"); VerifyTextfieldContextMenuContents(true, true, GetContextMenuModel()); } @@ -1026,12 +1019,12 @@ TEST_F(TextfieldTest, ReadOnlyTest) { EXPECT_STR_EQ("read only", textfield_->GetSelectedText()); // Cut should be disabled. - SetClipboardText("Test"); + SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test"); EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_CUT)); textfield_->ExecuteCommand(IDS_APP_CUT, 0); SendKeyEvent(ui::VKEY_X, false, true); SendKeyEvent(ui::VKEY_DELETE, true, false); - EXPECT_STR_EQ("Test", base::string16(GetClipboardText())); + EXPECT_STR_EQ("Test", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("read only", textfield_->text()); // Paste should be disabled. @@ -1042,16 +1035,16 @@ TEST_F(TextfieldTest, ReadOnlyTest) { EXPECT_STR_EQ("read only", textfield_->text()); // Copy should work normally. - SetClipboardText("Test"); + SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test"); EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_COPY)); textfield_->ExecuteCommand(IDS_APP_COPY, 0); - EXPECT_STR_EQ("read only", base::string16(GetClipboardText())); - SetClipboardText("Test"); + EXPECT_STR_EQ("read only", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test"); SendKeyEvent(ui::VKEY_C, false, true); - EXPECT_STR_EQ("read only", base::string16(GetClipboardText())); - SetClipboardText("Test"); + EXPECT_STR_EQ("read only", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test"); SendKeyEvent(ui::VKEY_INSERT, false, true); - EXPECT_STR_EQ("read only", base::string16(GetClipboardText())); + EXPECT_STR_EQ("read only", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); // SetText should work even in read only mode. textfield_->SetText(ASCIIToUTF16(" four five six ")); @@ -1276,24 +1269,24 @@ TEST_F(TextfieldTest, CutCopyPaste) { textfield_->SelectAll(false); EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_CUT)); textfield_->ExecuteCommand(IDS_APP_CUT, 0); - EXPECT_STR_EQ("123", base::string16(GetClipboardText())); + EXPECT_STR_EQ("123", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("", textfield_->text()); // Ensure [Ctrl]+[x] cuts and [Ctrl]+[Alt][x] does nothing. textfield_->SetText(ASCIIToUTF16("456")); textfield_->SelectAll(false); SendKeyEvent(ui::VKEY_X, true, false, true, false); - EXPECT_STR_EQ("123", base::string16(GetClipboardText())); + EXPECT_STR_EQ("123", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("456", textfield_->text()); SendKeyEvent(ui::VKEY_X, false, true); - EXPECT_STR_EQ("456", base::string16(GetClipboardText())); + EXPECT_STR_EQ("456", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("", textfield_->text()); // Ensure [Shift]+[Delete] cuts. textfield_->SetText(ASCIIToUTF16("123")); textfield_->SelectAll(false); SendKeyEvent(ui::VKEY_DELETE, true, false); - EXPECT_STR_EQ("123", base::string16(GetClipboardText())); + EXPECT_STR_EQ("123", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("", textfield_->text()); // Ensure IDS_APP_COPY copies. @@ -1301,26 +1294,26 @@ TEST_F(TextfieldTest, CutCopyPaste) { textfield_->SelectAll(false); EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_COPY)); textfield_->ExecuteCommand(IDS_APP_COPY, 0); - EXPECT_STR_EQ("789", base::string16(GetClipboardText())); + EXPECT_STR_EQ("789", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); // Ensure [Ctrl]+[c] copies and [Ctrl]+[Alt][c] does nothing. textfield_->SetText(ASCIIToUTF16("012")); textfield_->SelectAll(false); SendKeyEvent(ui::VKEY_C, true, false, true, false); - EXPECT_STR_EQ("789", base::string16(GetClipboardText())); + EXPECT_STR_EQ("789", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); SendKeyEvent(ui::VKEY_C, false, true); - EXPECT_STR_EQ("012", base::string16(GetClipboardText())); + EXPECT_STR_EQ("012", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); // Ensure [Ctrl]+[Insert] copies. textfield_->SetText(ASCIIToUTF16("345")); textfield_->SelectAll(false); SendKeyEvent(ui::VKEY_INSERT, false, true); - EXPECT_STR_EQ("345", base::string16(GetClipboardText())); + EXPECT_STR_EQ("345", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("345", textfield_->text()); // Ensure IDS_APP_PASTE, [Ctrl]+[V], and [Shift]+[Insert] pastes; // also ensure that [Ctrl]+[Alt]+[V] does nothing. - SetClipboardText("abc"); + SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "abc"); textfield_->SetText(base::string16()); EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_PASTE)); textfield_->ExecuteCommand(IDS_APP_PASTE, 0); @@ -1335,7 +1328,7 @@ TEST_F(TextfieldTest, CutCopyPaste) { // Ensure [Ctrl]+[Shift]+[Insert] is a no-op. textfield_->SelectAll(false); SendKeyEvent(ui::VKEY_INSERT, true, true); - EXPECT_STR_EQ("abc", base::string16(GetClipboardText())); + EXPECT_STR_EQ("abc", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("abcabcabc", textfield_->text()); } @@ -1747,6 +1740,109 @@ TEST_F(TextfieldTest, KeepInitiallySelectedWord) { EXPECT_EQ(gfx::Range(7, 0), textfield_->GetSelectedRange()); } +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) +TEST_F(TextfieldTest, SelectionClipboard) { + InitTextfield(); + textfield_->SetText(ASCIIToUTF16("0123")); + gfx::Point point_1(GetCursorPositionX(1), 0); + gfx::Point point_2(GetCursorPositionX(2), 0); + gfx::Point point_3(GetCursorPositionX(3), 0); + gfx::Point point_4(GetCursorPositionX(4), 0); + + // Text selected by the mouse should be placed on the selection clipboard. + ui::MouseEvent press(ui::ET_MOUSE_PRESSED, point_1, point_1, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMousePressed(press); + ui::MouseEvent drag(ui::ET_MOUSE_DRAGGED, point_3, point_3, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMouseDragged(drag); + ui::MouseEvent release(ui::ET_MOUSE_RELEASED, point_3, point_3, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMouseReleased(release); + EXPECT_EQ(gfx::Range(1, 3), textfield_->GetSelectedRange()); + EXPECT_STR_EQ("12", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + + // Select-all should update the selection clipboard. + SendKeyEvent(ui::VKEY_A, false, true); + EXPECT_EQ(gfx::Range(0, 4), textfield_->GetSelectedRange()); + EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + + // Shift-click selection modifications should update the clipboard. + NonClientMouseClick(); + ui::MouseEvent press_2(ui::ET_MOUSE_PRESSED, point_2, point_2, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + press_2.set_flags(press_2.flags() | ui::EF_SHIFT_DOWN); + textfield_->OnMousePressed(press_2); + ui::MouseEvent release_2(ui::ET_MOUSE_RELEASED, point_2, point_2, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMouseReleased(release_2); + EXPECT_EQ(gfx::Range(0, 2), textfield_->GetSelectedRange()); + EXPECT_STR_EQ("01", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + + // Shift-Left/Right should update the selection clipboard. + SendKeyEvent(ui::VKEY_RIGHT, true, false); + EXPECT_STR_EQ("012", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + SendKeyEvent(ui::VKEY_LEFT, true, false); + EXPECT_STR_EQ("01", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + SendKeyEvent(ui::VKEY_RIGHT, true, true); + EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + + // Moving the cursor without a selection should not change the clipboard. + SendKeyEvent(ui::VKEY_LEFT, false, false); + EXPECT_EQ(gfx::Range(0, 0), textfield_->GetSelectedRange()); + EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + + // Middle clicking should paste at the mouse (not cursor) location. + ui::MouseEvent middle(ui::ET_MOUSE_PRESSED, point_4, point_4, + ui::EF_MIDDLE_MOUSE_BUTTON, ui::EF_MIDDLE_MOUSE_BUTTON); + textfield_->OnMousePressed(middle); + EXPECT_STR_EQ("01230123", textfield_->text()); + EXPECT_EQ(gfx::Range(0, 0), textfield_->GetSelectedRange()); + EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + + // Middle click pasting should adjust trailing cursors. + textfield_->SelectRange(gfx::Range(5, 5)); + textfield_->OnMousePressed(middle); + EXPECT_STR_EQ("012301230123", textfield_->text()); + EXPECT_EQ(gfx::Range(9, 9), textfield_->GetSelectedRange()); + + // Middle click pasting should adjust trailing selections. + textfield_->SelectRange(gfx::Range(7, 9)); + textfield_->OnMousePressed(middle); + EXPECT_STR_EQ("0123012301230123", textfield_->text()); + EXPECT_EQ(gfx::Range(11, 13), textfield_->GetSelectedRange()); + + // Middle clicking in the selection should clear the clipboard and selection. + textfield_->SelectRange(gfx::Range(2, 6)); + textfield_->OnMousePressed(middle); + EXPECT_STR_EQ("0123012301230123", textfield_->text()); + EXPECT_EQ(gfx::Range(6, 6), textfield_->GetSelectedRange()); + EXPECT_TRUE(GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION).empty()); + + // Double and triple clicking should update the clipboard contents. + textfield_->SetText(ASCIIToUTF16("ab cd ef")); + gfx::Point word(GetCursorPositionX(4), 0); + ui::MouseEvent press_word(ui::ET_MOUSE_PRESSED, word, word, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMousePressed(press_word); + ui::MouseEvent release_word(ui::ET_MOUSE_RELEASED, word, word, + ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMouseReleased(release_word); + ui::MouseEvent double_click(ui::ET_MOUSE_PRESSED, word, word, + ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_DOUBLE_CLICK, + ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMousePressed(double_click); + textfield_->OnMouseReleased(release_word); + EXPECT_EQ(gfx::Range(3, 5), textfield_->GetSelectedRange()); + EXPECT_STR_EQ("cd", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + textfield_->OnMousePressed(press_word); + textfield_->OnMouseReleased(release_word); + EXPECT_EQ(gfx::Range(0, 8), textfield_->GetSelectedRange()); + EXPECT_STR_EQ("ab cd ef", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); +} +#endif + // Touch selection and dragging currently only works for chromeos. #if defined(OS_CHROMEOS) TEST_F(TextfieldTest, TouchSelectionAndDraggingTest) { |