Chromium Code Reviews| Index: ui/views/controls/textfield/textfield.cc |
| diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc |
| index 93cc7b3c6eb95e988e0791d48682cb3f0df43035..33889a05506f5a5c9bf83b52afd04e0ae7406d88 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" |
| @@ -53,6 +54,15 @@ void ConvertRectToScreen(const views::View* src, gfx::Rect* r) { |
| r->set_origin(new_origin); |
| } |
| +void UpdateSelectionClipboard(const base::string16& text) { |
|
oshima
2014/01/22 21:40:55
do you need to pass text? Can't you just use GetSe
msw
2014/01/22 23:15:35
I changed this file-local to be a class function a
|
| +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) |
| + if (!text.empty()) |
|
oshima
2014/01/22 21:40:55
nit: I believe you should have {} in this case.
msw
2014/01/22 23:15:35
Done.
|
| + ui::ScopedClipboardWriter( |
| + ui::Clipboard::GetForCurrentThread(), |
| + ui::CLIPBOARD_TYPE_SELECTION).WriteText(text); |
| +#endif |
| +} |
| + |
| } // namespace |
| namespace views { |
| @@ -151,8 +161,8 @@ base::i18n::TextDirection Textfield::GetTextDirection() const { |
| void Textfield::SelectAll(bool reversed) { |
| model_->SelectAll(reversed); |
| - OnCaretBoundsChanged(); |
| - SchedulePaint(); |
| + UpdateSelectionClipboard(text()); |
| + UpdateAfterChange(false, true); |
| } |
| base::string16 Textfield::GetSelectedText() const { |
| @@ -161,8 +171,7 @@ base::string16 Textfield::GetSelectedText() const { |
| void Textfield::ClearSelection() { |
| model_->ClearSelection(); |
| - OnCaretBoundsChanged(); |
| - SchedulePaint(); |
| + UpdateAfterChange(false, true); |
| } |
| bool Textfield::HasSelection() const { |
| @@ -241,10 +250,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 { |
| @@ -253,8 +259,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 { |
| @@ -370,6 +375,7 @@ bool Textfield::OnKeyPressed(const ui::KeyEvent& event) { |
| case ui::VKEY_A: |
| if (control && !alt) { |
| model_->SelectAll(false); |
| + UpdateSelectionClipboard(text()); |
| cursor_changed = true; |
| } |
| break; |
| @@ -397,6 +403,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(GetSelectedText()); |
| cursor_changed = render_text->selection() != selection_range; |
| break; |
| } |
| @@ -407,6 +414,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(GetSelectedText()); |
|
oshima
2014/01/22 21:40:55
Moving cursor clears the selection, so this should
msw
2014/01/22 23:15:35
If |shift| is true, MoveCursor may yield a selecte
|
| cursor_changed = true; |
| break; |
| case ui::VKEY_BACK: |
| @@ -460,7 +468,6 @@ bool Textfield::OnKeyPressed(const ui::KeyEvent& event) { |
| } |
| bool Textfield::OnMousePressed(const ui::MouseEvent& event) { |
| - OnBeforeUserAction(); |
| TrackMouseClicks(event); |
| if (!controller_ || !controller_->HandleMouseEvent(this, event)) { |
| @@ -468,36 +475,46 @@ bool Textfield::OnMousePressed(const ui::MouseEvent& event) { |
| RequestFocus(); |
| 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()); |
|
oshima
2014/01/22 21:40:55
.. and you can just call UpdateSelectionClipboard(
msw
2014/01/22 23:15:35
No; here we specifically want to clear the selecti
|
| + OnAfterUserAction(); |
| + } else if(!read_only()) { |
| + PasteSelectionClipboard(event); |
| + } |
| + } |
| +#endif |
| } |
| - OnAfterUserAction(); |
| touch_selection_controller_.reset(); |
| return true; |
| } |
| @@ -510,34 +527,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(GetSelectedText()); |
| OnAfterUserAction(); |
| } |
| @@ -637,16 +653,14 @@ void Textfield::OnGestureEvent(ui::GestureEvent* event) { |
| RequestFocus(); |
| // 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; |
| @@ -682,8 +696,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(); |
| @@ -892,9 +905,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(); |
| } |
| @@ -978,46 +989,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(); |
| } |
| //////////////////////////////////////////////////////////////////////////////// |
| @@ -1193,7 +1190,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(); |
| @@ -1357,11 +1353,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() { |
| @@ -1470,4 +1464,29 @@ void Textfield::CreateTouchSelectionControllerAndNotifyIt() { |
| touch_selection_controller_->SelectionChanged(); |
| } |
| +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 |