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 4163c40a3742095f01fec3da7cf14d44db94aae4..8798b24aef282358bd8e0a89efd713317d2d48ac 100644 |
| --- a/ui/views/controls/textfield/textfield.cc |
| +++ b/ui/views/controls/textfield/textfield.cc |
| @@ -40,9 +40,9 @@ |
| #include "ui/views/controls/native/native_view_host.h" |
| #include "ui/views/controls/textfield/textfield_controller.h" |
| #include "ui/views/drag_utils.h" |
| -#include "ui/views/metrics.h" |
| #include "ui/views/native_cursor.h" |
| #include "ui/views/painter.h" |
| +#include "ui/views/selection_controller.h" |
| #include "ui/views/style/platform_style.h" |
| #include "ui/views/views_delegate.h" |
| #include "ui/views/widget/widget.h" |
| @@ -85,18 +85,6 @@ void ConvertRectToScreen(const View* src, gfx::Rect* r) { |
| r->set_origin(new_origin); |
| } |
| -// Get the drag selection timer delay, respecting animation scaling for testing. |
| -int GetDragSelectionDelay() { |
| - switch (ui::ScopedAnimationDurationScaleMode::duration_scale_mode()) { |
| - case ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION: return 100; |
| - case ui::ScopedAnimationDurationScaleMode::FAST_DURATION: return 25; |
| - case ui::ScopedAnimationDurationScaleMode::SLOW_DURATION: return 400; |
| - case ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION: return 1; |
| - case ui::ScopedAnimationDurationScaleMode::ZERO_DURATION: return 0; |
| - } |
| - return 100; |
| -} |
| - |
| // Get the default command for a given key |event|. |
| ui::TextEditCommand GetCommandForKeyEvent(const ui::KeyEvent& event) { |
| if (event.type() != ui::ET_KEY_PRESSED || event.IsUnicodeKeyCode()) |
| @@ -256,10 +244,11 @@ Textfield::Textfield() |
| text_input_type_(ui::TEXT_INPUT_TYPE_TEXT), |
| text_input_flags_(0), |
| performing_user_action_(false), |
| + performing_mouse_action_(false), |
| skip_input_method_cancel_composition_(false), |
| drop_cursor_visible_(false), |
| initiating_drag_(false), |
| - aggregated_clicks_(0), |
| + selection_controller_(new SelectionController(this)), |
| drag_start_display_offset_(0), |
| touch_handles_hidden_due_to_scroll_(false), |
| use_focus_ring_(ui::MaterialDesignController::IsSecondaryUiMaterial()), |
| @@ -335,7 +324,8 @@ base::string16 Textfield::GetSelectedText() const { |
| void Textfield::SelectAll(bool reversed) { |
| model_->SelectAll(reversed); |
| - UpdateSelectionClipboard(); |
| + if (HasSelection()) |
| + UpdateSelectionClipboard(); |
| UpdateAfterChange(false, true); |
| } |
| @@ -545,10 +535,6 @@ void Textfield::SetAccessibleName(const base::string16& name) { |
| accessible_name_ = name; |
| } |
| -bool Textfield::HasTextBeingDragged() { |
| - return initiating_drag_; |
| -} |
| - |
| //////////////////////////////////////////////////////////////////////////////// |
| // Textfield, View overrides: |
| @@ -587,88 +573,21 @@ gfx::NativeCursor Textfield::GetCursor(const ui::MouseEvent& event) { |
| } |
| bool Textfield::OnMousePressed(const ui::MouseEvent& event) { |
| - TrackMouseClicks(event); |
| - |
| - if (!controller_ || !controller_->HandleMouseEvent(this, event)) { |
| - if (event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton()) { |
| - RequestFocus(); |
| - ShowImeIfNeeded(); |
| - } |
| - |
| - if (event.IsOnlyLeftMouseButton()) { |
| - OnBeforeUserAction(); |
| - initiating_drag_ = false; |
| - switch (aggregated_clicks_) { |
| - case 0: |
| - if (GetRenderText()->IsPointInSelection(event.location())) |
| - initiating_drag_ = true; |
| - else |
| - MoveCursorTo(event.location(), event.IsShiftDown()); |
| - break; |
| - case 1: |
| - SelectWordAt(event.location()); |
| - double_click_word_ = GetRenderText()->selection(); |
| - break; |
| - case 2: |
| - SelectAll(false); |
| - break; |
| - default: |
| - NOTREACHED(); |
| - } |
| - OnAfterUserAction(); |
| - } |
| - |
| -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) |
| - if (event.IsOnlyMiddleMouseButton()) { |
| - if (GetRenderText()->IsPointInSelection(event.location())) { |
| - OnBeforeUserAction(); |
| - ClearSelection(); |
| - ui::ScopedClipboardWriter( |
| - ui::CLIPBOARD_TYPE_SELECTION).WriteText(base::string16()); |
| - OnAfterUserAction(); |
| - } else if (!read_only()) { |
| - PasteSelectionClipboard(event); |
| - } |
| - } |
| -#endif |
| + bool handled = controller_ && controller_->HandleMouseEvent(this, event); |
| + if (!handled && |
| + (event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton())) { |
| + RequestFocus(); |
| + ShowImeIfNeeded(); |
| } |
| - |
| - return true; |
| + return selection_controller_->OnMousePressed(event, handled); |
| } |
| bool Textfield::OnMouseDragged(const ui::MouseEvent& event) { |
| - last_drag_location_ = event.location(); |
| - |
| - // Don't adjust the cursor on a potential drag and drop. |
| - if (initiating_drag_ || !event.IsOnlyLeftMouseButton()) |
| - return true; |
| - |
| - // A timer is used to continuously scroll while selecting beyond side edges. |
| - const int x = event.location().x(); |
| - if ((x >= 0 && x <= width()) || GetDragSelectionDelay() == 0) { |
| - drag_selection_timer_.Stop(); |
| - SelectThroughLastDragLocation(); |
| - } else if (!drag_selection_timer_.IsRunning()) { |
| - // Select through the edge of the visible text, then start the scroll timer. |
| - last_drag_location_.set_x(std::min(std::max(0, x), width())); |
| - SelectThroughLastDragLocation(); |
| - drag_selection_timer_.Start( |
| - FROM_HERE, base::TimeDelta::FromMilliseconds(GetDragSelectionDelay()), |
| - this, &Textfield::SelectThroughLastDragLocation); |
| - } |
| - |
| - return true; |
| + return selection_controller_->OnMouseDragged(event); |
| } |
| void Textfield::OnMouseReleased(const ui::MouseEvent& event) { |
| - OnBeforeUserAction(); |
| - drag_selection_timer_.Stop(); |
| - // Cancel suspected drag initiations, the user was clicking in the selection. |
| - if (initiating_drag_) |
| - MoveCursorTo(event.location(), false); |
| - initiating_drag_ = false; |
| - UpdateSelectionClipboard(); |
| - OnAfterUserAction(); |
| + selection_controller_->OnMouseReleased(event); |
| } |
| WordLookupClient* Textfield::GetWordLookupClient() { |
| @@ -1136,6 +1055,13 @@ bool Textfield::GetDecoratedWordAtPoint(const gfx::Point& point, |
| } |
| //////////////////////////////////////////////////////////////////////////////// |
| +// Textfield, SelectionControllerDelegate overrides: |
| + |
| +bool Textfield::HasTextBeingDragged() const { |
| + return initiating_drag_; |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| // Textfield, ui::TouchEditable overrides: |
| void Textfield::SelectRect(const gfx::Point& start, const gfx::Point& end) { |
| @@ -1601,6 +1527,10 @@ gfx::RenderText* Textfield::GetRenderText() const { |
| return model_->render_text(); |
| } |
| +gfx::Point Textfield::GetLastClickLocation() const { |
| + return selection_controller_->last_click_location(); |
| +} |
| + |
| base::string16 Textfield::GetSelectionClipboardText() const { |
| base::string16 selection_clipboard_text; |
| ui::Clipboard::GetForCurrentThread()->ReadText( |
| @@ -1799,7 +1729,7 @@ void Textfield::ExecuteTextEditCommand(ui::TextEditCommand command) { |
| } |
| cursor_changed |= GetSelectionModel() != selection_model; |
| - if (cursor_changed) |
| + if (cursor_changed && HasSelection()) |
| UpdateSelectionClipboard(); |
| UpdateAfterChange(text_changed, cursor_changed); |
| OnAfterUserAction(); |
| @@ -1808,6 +1738,97 @@ void Textfield::ExecuteTextEditCommand(ui::TextEditCommand command) { |
| //////////////////////////////////////////////////////////////////////////////// |
| // Textfield, private: |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// Textfield, SelectionControllerDelegate overrides: |
| + |
| +gfx::RenderText* Textfield::GetRenderTextForSelectionController() { |
| + return GetRenderText(); |
| +} |
| + |
| +bool Textfield::IsReadOnly() const { |
| + return read_only(); |
| +} |
| + |
| +void Textfield::SetTextBeingDragged(bool value) { |
| + initiating_drag_ = value; |
| +} |
| + |
| +int Textfield::GetViewHeight() const { |
| + return height(); |
| +} |
| + |
| +int Textfield::GetViewWidth() const { |
| + return width(); |
| +} |
| + |
| +int Textfield::GetDragSelectionDelay() const { |
| + switch (ui::ScopedAnimationDurationScaleMode::duration_scale_mode()) { |
| + case ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION: |
| + return 100; |
| + case ui::ScopedAnimationDurationScaleMode::FAST_DURATION: |
| + return 25; |
| + case ui::ScopedAnimationDurationScaleMode::SLOW_DURATION: |
| + return 400; |
| + case ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION: |
| + return 1; |
| + case ui::ScopedAnimationDurationScaleMode::ZERO_DURATION: |
| + return 0; |
| + } |
| + return 100; |
| +} |
| + |
| +void Textfield::OnBeforeMouseAction() { |
| + OnBeforeUserAction(); |
| + DCHECK(!performing_mouse_action_); |
| + performing_mouse_action_ = true; |
| +} |
| + |
| +void Textfield::OnAfterMouseAction(bool text_changed, bool selection_changed) { |
| + OnAfterUserAction(); |
| + DCHECK(performing_mouse_action_); |
| + performing_mouse_action_ = false; |
| + UpdateAfterChange(text_changed, selection_changed); |
| +} |
| + |
| +void Textfield::OnBeforeSelectionUpdated() { |
|
msw
2016/10/18 23:15:05
nit: rename these to OnBeforeSelectionUpdate and O
karandeepb
2016/10/20 04:12:57
Done. (Obsolete)
|
| + DCHECK(performing_mouse_action_); |
| + if (model_->HasCompositionText()) |
| + model_->ConfirmCompositionText(); |
| +} |
| + |
| +void Textfield::OnAfterSelectionUpdated() { |
| + DCHECK(performing_mouse_action_); |
| + UpdateAfterChange(false, true); |
| +} |
| + |
| +void Textfield::PasteSelectionClipboard(const ui::MouseEvent& event) { |
| + if (!event.IsOnlyMiddleMouseButton() || read_only() || |
| + !performing_user_action_) |
| + return; |
| + base::string16 selection_clipboard_text = GetSelectionClipboardText(); |
| + const gfx::SelectionModel mouse = |
| + GetRenderText()->FindCursorPosition(event.location()); |
| + if (!HasFocus()) |
| + RequestFocus(); |
| + model_->MoveCursorTo(mouse); |
| + if (!selection_clipboard_text.empty()) { |
| + model_->InsertText(selection_clipboard_text); |
| + UpdateAfterChange(true, true); |
| + } |
| +} |
| + |
| +void Textfield::UpdateSelectionClipboard() { |
| +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) |
| + if (performing_user_action_ && |
| + text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD) { |
| + ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_SELECTION) |
| + .WriteText(GetSelectedText()); |
| + if (controller_) |
| + controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_SELECTION); |
| + } |
| +#endif |
| +} |
| + |
| void Textfield::AccessibilitySetValue(const base::string16& new_value) { |
| if (!read_only()) { |
| SetText(new_value); |
| @@ -1840,6 +1861,8 @@ void Textfield::UpdateBorder() { |
| } |
| void Textfield::UpdateAfterChange(bool text_changed, bool cursor_changed) { |
| + if (performing_mouse_action_) |
| + return; |
| if (text_changed) { |
| if (controller_) |
| controller_->ContentsChanged(this, text()); |
| @@ -1898,39 +1921,6 @@ void Textfield::MoveCursorTo(const gfx::Point& point, bool select) { |
| UpdateAfterChange(false, true); |
| } |
| -void Textfield::SelectThroughLastDragLocation() { |
| - OnBeforeUserAction(); |
| - |
| - const bool drags_to_end = PlatformStyle::kTextfieldDragVerticallyDragsToEnd; |
| - if (drags_to_end && last_drag_location_.y() < 0) { |
| - model_->MoveCursor(gfx::BreakType::LINE_BREAK, |
| - gfx::VisualCursorDirection::CURSOR_LEFT, |
| - gfx::SELECTION_RETAIN); |
| - } else if (drags_to_end && last_drag_location_.y() > height()) { |
| - model_->MoveCursor(gfx::BreakType::LINE_BREAK, |
| - gfx::VisualCursorDirection::CURSOR_RIGHT, |
| - gfx::SELECTION_RETAIN); |
| - } else { |
| - model_->MoveCursorTo(last_drag_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(); |
| -} |
| - |
| void Textfield::OnCaretBoundsChanged() { |
| if (GetInputMethod()) |
| GetInputMethod()->OnCaretBoundsChanged(this); |
| @@ -2004,24 +1994,6 @@ void Textfield::UpdateContextMenu() { |
| MenuRunner::ASYNC)); |
| } |
| -void Textfield::TrackMouseClicks(const ui::MouseEvent& event) { |
| - if (event.IsOnlyLeftMouseButton()) { |
| - base::TimeDelta time_delta = event.time_stamp() - last_click_time_; |
| - if (!last_click_time_.is_null() && |
| - time_delta.InMilliseconds() <= GetDoubleClickInterval() && |
| - !ExceededDragThreshold(event.location() - last_click_location_)) { |
| - // Upon clicking after a triple click, the count should go back to double |
| - // click and alternate between double and triple. This assignment maps |
| - // 0 to 1, 1 to 2, 2 to 1. |
| - aggregated_clicks_ = (aggregated_clicks_ % 2) + 1; |
| - } else { |
| - aggregated_clicks_ = 0; |
| - } |
| - last_click_time_ = event.time_stamp(); |
| - last_click_location_ = event.location(); |
| - } |
| -} |
| - |
| bool Textfield::ImeEditingAllowed() const { |
| // Disallow input method editing of password fields. |
| ui::TextInputType t = GetTextInputType(); |
| @@ -2051,35 +2023,6 @@ void Textfield::CreateTouchSelectionControllerAndNotifyIt() { |
| touch_selection_controller_->SelectionChanged(); |
| } |
| -void Textfield::UpdateSelectionClipboard() const { |
| -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) |
| - if (performing_user_action_ && HasSelection() && |
| - text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD) { |
| - ui::ScopedClipboardWriter( |
| - ui::CLIPBOARD_TYPE_SELECTION).WriteText(GetSelectedText()); |
| - if (controller_) |
| - controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_SELECTION); |
| - } |
| -#endif |
| -} |
| - |
| -void Textfield::PasteSelectionClipboard(const ui::MouseEvent& event) { |
| - DCHECK(event.IsOnlyMiddleMouseButton()); |
| - DCHECK(!read_only()); |
| - base::string16 selection_clipboard_text = GetSelectionClipboardText(); |
| - OnBeforeUserAction(); |
| - const gfx::SelectionModel mouse = |
| - GetRenderText()->FindCursorPosition(event.location()); |
| - if (!HasFocus()) |
| - RequestFocus(); |
| - model_->MoveCursorTo(mouse); |
| - if (!selection_clipboard_text.empty()) { |
| - model_->InsertText(selection_clipboard_text); |
| - UpdateAfterChange(true, true); |
| - } |
| - OnAfterUserAction(); |
| -} |
| - |
| void Textfield::OnEditFailed() { |
| PlatformStyle::OnTextfieldEditFailed(); |
| } |