Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(862)

Unified Diff: ui/views/controls/textfield/textfield.cc

Issue 135863002: Reland Merge NativeTextfieldViews into views::Textfield. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Handle Ctrl-Shift-Delete and Backspace on Linux, like on ChromeOS. Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ui/views/controls/textfield/textfield.h ('k') | ui/views/controls/textfield/textfield_controller.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ui/views/controls/textfield/textfield.cc
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index 69adbb771fcd99e29d7deefda4c758b1e6aaeeb2..afef279f96a09c7733c7d58507388e5ac9168c43 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -6,34 +6,52 @@
#include <string>
-#include "base/command_line.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
+#include "base/debug/trace_event.h"
+#include "base/i18n/case_conversion.h"
+#include "grit/ui_strings.h"
#include "ui/base/accessibility/accessible_view_state.h"
-#include "ui/base/ime/text_input_type.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/drag_utils.h"
#include "ui/base/resource/resource_bundle.h"
-#include "ui/base/ui_base_switches.h"
+#include "ui/base/ui_base_switches_util.h"
#include "ui/events/event.h"
#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/gfx/canvas.h"
#include "ui/gfx/insets.h"
-#include "ui/gfx/range/range.h"
-#include "ui/gfx/selection_model.h"
#include "ui/native_theme/native_theme.h"
+#include "ui/views/background.h"
+#include "ui/views/controls/focusable_border.h"
+#include "ui/views/controls/menu/menu_item_view.h"
+#include "ui/views/controls/menu/menu_model_adapter.h"
+#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/controls/native/native_view_host.h"
-#include "ui/views/controls/textfield/native_textfield_views.h"
#include "ui/views/controls/textfield/textfield_controller.h"
+#include "ui/views/drag_utils.h"
+#include "ui/views/ime/input_method.h"
+#include "ui/views/metrics.h"
#include "ui/views/painter.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/widget.h"
+#if defined(USE_AURA)
+#include "ui/base/cursor/cursor.h"
+#endif
+
+#if defined(OS_WIN) && defined(USE_AURA)
+#include "base/win/win_util.h"
+#endif
+
namespace {
// Default placeholder text color.
const SkColor kDefaultPlaceholderTextColor = SK_ColorLTGRAY;
-gfx::FontList GetDefaultFontList() {
- return ResourceBundle::GetSharedInstance().GetFontList(
- ResourceBundle::BaseFont);
+void ConvertRectToScreen(const views::View* src, gfx::Rect* r) {
+ DCHECK(src);
+
+ gfx::Point new_origin = r->origin();
+ views::View::ConvertPointToScreen(src, &new_origin);
+ r->set_origin(new_origin);
}
} // namespace
@@ -55,22 +73,26 @@ size_t Textfield::GetCaretBlinkMs() {
}
Textfield::Textfield()
- : textfield_view_(NULL),
+ : model_(new TextfieldViewsModel(this)),
controller_(NULL),
style_(STYLE_DEFAULT),
- font_list_(GetDefaultFontList()),
read_only_(false),
default_width_in_chars_(0),
- draw_border_(true),
text_color_(SK_ColorBLACK),
use_default_text_color_(true),
background_color_(SK_ColorWHITE),
use_default_background_color_(true),
- horizontal_margins_were_set_(false),
- vertical_margins_were_set_(false),
placeholder_text_color_(kDefaultPlaceholderTextColor),
text_input_type_(ui::TEXT_INPUT_TYPE_TEXT),
+ skip_input_method_cancel_composition_(false),
+ is_cursor_visible_(false),
+ is_drop_cursor_visible_(false),
+ initiating_drag_(false),
+ aggregated_clicks_(0),
weak_ptr_factory_(this) {
+ set_context_menu_controller(this);
+ set_drag_controller(this);
+ set_border(new FocusableBorder());
SetFocusable(true);
if (ViewsDelegate::views_delegate) {
@@ -83,23 +105,28 @@ Textfield::Textfield()
}
Textfield::Textfield(StyleFlags style)
- : textfield_view_(NULL),
+ : model_(new TextfieldViewsModel(this)),
controller_(NULL),
style_(style),
- font_list_(GetDefaultFontList()),
read_only_(false),
default_width_in_chars_(0),
- draw_border_(true),
text_color_(SK_ColorBLACK),
use_default_text_color_(true),
background_color_(SK_ColorWHITE),
use_default_background_color_(true),
- horizontal_margins_were_set_(false),
- vertical_margins_were_set_(false),
placeholder_text_color_(kDefaultPlaceholderTextColor),
text_input_type_(ui::TEXT_INPUT_TYPE_TEXT),
+ skip_input_method_cancel_composition_(false),
+ is_cursor_visible_(false),
+ is_drop_cursor_visible_(false),
+ initiating_drag_(false),
+ aggregated_clicks_(0),
weak_ptr_factory_(this) {
+ set_context_menu_controller(this);
+ set_drag_controller(this);
+ set_border(new FocusableBorder());
SetFocusable(true);
+
if (IsObscured())
SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
@@ -126,11 +153,10 @@ TextfieldController* Textfield::GetController() const {
void Textfield::SetReadOnly(bool read_only) {
// Update read-only without changing the focusable state (or active, etc.).
read_only_ = read_only;
- if (textfield_view_) {
- textfield_view_->UpdateReadOnly();
- textfield_view_->UpdateTextColor();
- textfield_view_->UpdateBackgroundColor();
- }
+ if (GetInputMethod())
+ GetInputMethod()->OnTextInputTypeChanged(this);
+ SetColor(GetTextColor());
+ UpdateBackgroundColor();
}
bool Textfield::IsObscured() const {
@@ -145,14 +171,11 @@ void Textfield::SetObscured(bool obscured) {
style_ = static_cast<StyleFlags>(style_ & ~STYLE_OBSCURED);
SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT);
}
- if (textfield_view_)
- textfield_view_->UpdateIsObscured();
-}
-
-ui::TextInputType Textfield::GetTextInputType() const {
- if (read_only() || !enabled())
- return ui::TEXT_INPUT_TYPE_NONE;
- return text_input_type_;
+ GetRenderText()->SetObscured(obscured);
+ OnCaretBoundsChanged();
+ if (GetInputMethod())
+ GetInputMethod()->OnTextInputTypeChanged(this);
+ SchedulePaint();
}
void Textfield::SetTextInputType(ui::TextInputType type) {
@@ -162,47 +185,51 @@ void Textfield::SetTextInputType(ui::TextInputType type) {
SetObscured(should_be_obscured);
}
-void Textfield::SetText(const base::string16& text) {
- text_ = text;
- if (textfield_view_)
- textfield_view_->UpdateText();
+void Textfield::SetText(const base::string16& new_text) {
+ model_->SetText(GetTextForDisplay(new_text));
+ OnCaretBoundsChanged();
+ SchedulePaint();
+ NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_TEXT_CHANGED, true);
}
-void Textfield::AppendText(const base::string16& text) {
- text_ += text;
- if (textfield_view_)
- textfield_view_->AppendText(text);
+void Textfield::AppendText(const base::string16& new_text) {
+ if (new_text.empty())
+ return;
+ model_->Append(GetTextForDisplay(new_text));
+ OnCaretBoundsChanged();
+ SchedulePaint();
}
-void Textfield::InsertOrReplaceText(const base::string16& text) {
- if (textfield_view_) {
- textfield_view_->InsertOrReplaceText(text);
- text_ = textfield_view_->GetText();
- }
+void Textfield::InsertOrReplaceText(const base::string16& new_text) {
+ if (new_text.empty())
+ return;
+ model_->InsertText(new_text);
+ OnCaretBoundsChanged();
+ SchedulePaint();
}
base::i18n::TextDirection Textfield::GetTextDirection() const {
- return textfield_view_ ?
- textfield_view_->GetTextDirection() : base::i18n::UNKNOWN_DIRECTION;
+ return GetRenderText()->GetTextDirection();
}
void Textfield::SelectAll(bool reversed) {
- if (textfield_view_)
- textfield_view_->SelectAll(reversed);
+ model_->SelectAll(reversed);
+ OnCaretBoundsChanged();
+ SchedulePaint();
}
base::string16 Textfield::GetSelectedText() const {
- return textfield_view_ ? textfield_view_->GetSelectedText() :
- base::string16();
+ return model_->GetSelectedText();
}
-void Textfield::ClearSelection() const {
- if (textfield_view_)
- textfield_view_->ClearSelection();
+void Textfield::ClearSelection() {
+ model_->ClearSelection();
+ OnCaretBoundsChanged();
+ SchedulePaint();
}
bool Textfield::HasSelection() const {
- return textfield_view_ && !textfield_view_->GetSelectedRange().is_empty();
+ return !GetSelectedRange().is_empty();
}
SkColor Textfield::GetTextColor() const {
@@ -217,14 +244,12 @@ SkColor Textfield::GetTextColor() const {
void Textfield::SetTextColor(SkColor color) {
text_color_ = color;
use_default_text_color_ = false;
- if (textfield_view_)
- textfield_view_->UpdateTextColor();
+ SetColor(color);
}
void Textfield::UseDefaultTextColor() {
use_default_text_color_ = true;
- if (textfield_view_)
- textfield_view_->UpdateTextColor();
+ SetColor(GetTextColor());
}
SkColor Textfield::GetBackgroundColor() const {
@@ -239,165 +264,90 @@ SkColor Textfield::GetBackgroundColor() const {
void Textfield::SetBackgroundColor(SkColor color) {
background_color_ = color;
use_default_background_color_ = false;
- if (textfield_view_)
- textfield_view_->UpdateBackgroundColor();
+ UpdateBackgroundColor();
}
void Textfield::UseDefaultBackgroundColor() {
use_default_background_color_ = true;
- if (textfield_view_)
- textfield_view_->UpdateBackgroundColor();
+ UpdateBackgroundColor();
}
bool Textfield::GetCursorEnabled() const {
- return textfield_view_ && textfield_view_->GetCursorEnabled();
+ return GetRenderText()->cursor_enabled();
}
void Textfield::SetCursorEnabled(bool enabled) {
- if (textfield_view_)
- textfield_view_->SetCursorEnabled(enabled);
-}
-
-void Textfield::SetFontList(const gfx::FontList& font_list) {
- font_list_ = font_list;
- if (textfield_view_)
- textfield_view_->UpdateFont();
- PreferredSizeChanged();
+ GetRenderText()->SetCursorEnabled(enabled);
}
-const gfx::Font& Textfield::GetPrimaryFont() const {
- return font_list_.GetPrimaryFont();
-}
-
-void Textfield::SetFont(const gfx::Font& font) {
- SetFontList(gfx::FontList(font));
-}
-
-void Textfield::SetHorizontalMargins(int left, int right) {
- if (horizontal_margins_were_set_ &&
- left == margins_.left() && right == margins_.right()) {
- return;
- }
- margins_.Set(margins_.top(), left, margins_.bottom(), right);
- horizontal_margins_were_set_ = true;
- if (textfield_view_)
- textfield_view_->UpdateHorizontalMargins();
- PreferredSizeChanged();
+const gfx::FontList& Textfield::GetFontList() const {
+ return GetRenderText()->font_list();
}
-void Textfield::SetVerticalMargins(int top, int bottom) {
- if (vertical_margins_were_set_ &&
- top == margins_.top() && bottom == margins_.bottom()) {
- return;
- }
- margins_.Set(top, margins_.left(), bottom, margins_.right());
- vertical_margins_were_set_ = true;
- if (textfield_view_)
- textfield_view_->UpdateVerticalMargins();
+void Textfield::SetFontList(const gfx::FontList& font_list) {
+ GetRenderText()->SetFontList(font_list);
+ OnCaretBoundsChanged();
PreferredSizeChanged();
}
-void Textfield::RemoveBorder() {
- if (!draw_border_)
- return;
-
- draw_border_ = false;
- if (textfield_view_)
- textfield_view_->UpdateBorder();
-}
-
base::string16 Textfield::GetPlaceholderText() const {
return placeholder_text_;
}
-bool Textfield::GetHorizontalMargins(int* left, int* right) {
- if (!horizontal_margins_were_set_)
- return false;
-
- *left = margins_.left();
- *right = margins_.right();
- return true;
-}
-
-bool Textfield::GetVerticalMargins(int* top, int* bottom) {
- if (!vertical_margins_were_set_)
- return false;
-
- *top = margins_.top();
- *bottom = margins_.bottom();
- return true;
-}
-
-void Textfield::UpdateAllProperties() {
- if (textfield_view_) {
- textfield_view_->UpdateText();
- textfield_view_->UpdateTextColor();
- textfield_view_->UpdateBackgroundColor();
- textfield_view_->UpdateReadOnly();
- textfield_view_->UpdateFont();
- textfield_view_->UpdateEnabled();
- textfield_view_->UpdateBorder();
- textfield_view_->UpdateIsObscured();
- textfield_view_->UpdateHorizontalMargins();
- textfield_view_->UpdateVerticalMargins();
- }
-}
-
-void Textfield::SyncText() {
- if (textfield_view_) {
- base::string16 new_text = textfield_view_->GetText();
- if (new_text != text_) {
- text_ = new_text;
- if (controller_)
- controller_->ContentsChanged(this, text_);
- }
- }
-}
-
bool Textfield::IsIMEComposing() const {
- return textfield_view_ && textfield_view_->IsIMEComposing();
+ return model_->HasCompositionText();
}
const gfx::Range& Textfield::GetSelectedRange() const {
- return textfield_view_->GetSelectedRange();
+ return GetRenderText()->selection();
}
void Textfield::SelectRange(const gfx::Range& range) {
- textfield_view_->SelectRange(range);
+ model_->SelectRange(range);
+ OnCaretBoundsChanged();
+ SchedulePaint();
+ NotifyAccessibilityEvent(
+ ui::AccessibilityTypes::EVENT_SELECTION_CHANGED, true);
}
const gfx::SelectionModel& Textfield::GetSelectionModel() const {
- return textfield_view_->GetSelectionModel();
+ return GetRenderText()->selection_model();
}
void Textfield::SelectSelectionModel(const gfx::SelectionModel& sel) {
- textfield_view_->SelectSelectionModel(sel);
+ model_->SelectSelectionModel(sel);
+ OnCaretBoundsChanged();
+ SchedulePaint();
}
size_t Textfield::GetCursorPosition() const {
- return textfield_view_->GetCursorPosition();
+ return model_->GetCursorPosition();
}
void Textfield::SetColor(SkColor value) {
- return textfield_view_->SetColor(value);
+ GetRenderText()->SetColor(value);
+ SchedulePaint();
}
void Textfield::ApplyColor(SkColor value, const gfx::Range& range) {
- return textfield_view_->ApplyColor(value, range);
+ GetRenderText()->ApplyColor(value, range);
+ SchedulePaint();
}
void Textfield::SetStyle(gfx::TextStyle style, bool value) {
- return textfield_view_->SetStyle(style, value);
+ GetRenderText()->SetStyle(style, value);
+ SchedulePaint();
}
void Textfield::ApplyStyle(gfx::TextStyle style,
bool value,
const gfx::Range& range) {
- return textfield_view_->ApplyStyle(style, value, range);
+ GetRenderText()->ApplyStyle(style, value, range);
+ SchedulePaint();
}
void Textfield::ClearEditHistory() {
- textfield_view_->ClearEditHistory();
+ model_->ClearEditHistory();
}
void Textfield::SetAccessibleName(const base::string16& name) {
@@ -405,7 +355,7 @@ void Textfield::SetAccessibleName(const base::string16& name) {
}
void Textfield::ExecuteCommand(int command_id) {
- textfield_view_->ExecuteCommand(command_id, ui::EF_NONE);
+ ExecuteCommand(command_id, ui::EF_NONE);
}
void Textfield::SetFocusPainter(scoped_ptr<Painter> focus_painter) {
@@ -413,35 +363,20 @@ void Textfield::SetFocusPainter(scoped_ptr<Painter> focus_painter) {
}
bool Textfield::HasTextBeingDragged() {
- return textfield_view_->HasTextBeingDragged();
+ return initiating_drag_;
}
////////////////////////////////////////////////////////////////////////////////
// Textfield, View overrides:
-void Textfield::Layout() {
- if (textfield_view_) {
- textfield_view_->SetBoundsRect(GetContentsBounds());
- textfield_view_->Layout();
- }
-}
-
int Textfield::GetBaseline() const {
- gfx::Insets insets = GetTextInsets();
- const int baseline = textfield_view_ ?
- textfield_view_->GetTextfieldBaseline() : font_list_.GetBaseline();
- return insets.top() + baseline;
+ return GetInsets().top() + GetRenderText()->GetBaseline();
}
gfx::Size Textfield::GetPreferredSize() {
- gfx::Insets insets = GetTextInsets();
-
- const int font_height = textfield_view_ ? textfield_view_->GetFontHeight() :
- font_list_.GetHeight();
- return gfx::Size(
- GetPrimaryFont().GetExpectedTextWidth(default_width_in_chars_)
- + insets.width(),
- font_height + insets.height());
+ const gfx::Insets& insets = GetInsets();
+ return gfx::Size(GetFontList().GetExpectedTextWidth(default_width_in_chars_) +
+ insets.width(), GetFontList().GetHeight() + insets.height());
}
void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) {
@@ -455,35 +390,248 @@ bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) {
}
void Textfield::OnPaint(gfx::Canvas* canvas) {
- View::OnPaint(canvas);
+ OnPaintBackground(canvas);
+ PaintTextAndCursor(canvas);
+ OnPaintBorder(canvas);
if (NativeViewHost::kRenderNativeControlFocus)
Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
}
-bool Textfield::OnKeyPressed(const ui::KeyEvent& e) {
- return textfield_view_ && textfield_view_->HandleKeyPressed(e);
-}
+bool Textfield::OnKeyPressed(const ui::KeyEvent& event) {
+ bool handled = controller_ && controller_->HandleKeyEvent(this, event);
+ touch_selection_controller_.reset();
+ if (handled)
+ return true;
+
+ // TODO(oshima): Refactor and consolidate with ExecuteCommand.
+ if (event.type() == ui::ET_KEY_PRESSED) {
+ ui::KeyboardCode key_code = event.key_code();
+ if (key_code == ui::VKEY_TAB || event.IsUnicodeKeyCode())
+ return false;
+
+ gfx::RenderText* render_text = GetRenderText();
+ const bool editable = !read_only();
+ const bool readable = !IsObscured();
+ const bool shift = event.IsShiftDown();
+ const bool control = event.IsControlDown();
+ const bool alt = event.IsAltDown() || event.IsAltGrDown();
+ bool text_changed = false;
+ bool cursor_changed = false;
+
+ OnBeforeUserAction();
+ switch (key_code) {
+ case ui::VKEY_Z:
+ if (control && !shift && !alt && editable)
+ cursor_changed = text_changed = model_->Undo();
+ else if (control && shift && !alt && editable)
+ cursor_changed = text_changed = model_->Redo();
+ break;
+ case ui::VKEY_Y:
+ if (control && !alt && editable)
+ cursor_changed = text_changed = model_->Redo();
+ break;
+ case ui::VKEY_A:
+ if (control && !alt) {
+ model_->SelectAll(false);
+ cursor_changed = true;
+ }
+ break;
+ case ui::VKEY_X:
+ if (control && !alt && editable && readable)
+ cursor_changed = text_changed = Cut();
+ break;
+ case ui::VKEY_C:
+ if (control && !alt && readable)
+ Copy();
+ break;
+ case ui::VKEY_V:
+ if (control && !alt && editable)
+ cursor_changed = text_changed = Paste();
+ break;
+ case ui::VKEY_RIGHT:
+ case ui::VKEY_LEFT: {
+ // We should ignore the alt-left/right keys because alt key doesn't make
+ // any special effects for them and they can be shortcut keys such like
+ // forward/back of the browser history.
+ if (alt)
+ break;
+ const gfx::Range selection_range = render_text->selection();
+ model_->MoveCursor(
+ control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK,
+ (key_code == ui::VKEY_RIGHT) ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT,
+ shift);
+ cursor_changed = render_text->selection() != selection_range;
+ break;
+ }
+ case ui::VKEY_END:
+ case ui::VKEY_HOME:
+ if ((key_code == ui::VKEY_HOME) ==
+ (render_text->GetTextDirection() == base::i18n::RIGHT_TO_LEFT))
+ model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, shift);
+ else
+ model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, shift);
+ cursor_changed = true;
+ break;
+ case ui::VKEY_BACK:
+ case ui::VKEY_DELETE:
+ if (!editable)
+ break;
+ if (!model_->HasSelection()) {
+ gfx::VisualCursorDirection direction = (key_code == ui::VKEY_DELETE) ?
+ gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT;
+ if (shift && control) {
+ // If shift and control are pressed, erase up to the next line break
+ // on Linux and ChromeOS. Otherwise, do nothing.
+#if defined(OS_LINUX)
+ model_->MoveCursor(gfx::LINE_BREAK, direction, true);
+#else
+ break;
+#endif
+ } else if (control) {
+ // If only control is pressed, then erase the previous/next word.
+ model_->MoveCursor(gfx::WORD_BREAK, direction, true);
+ }
+ }
+ if (key_code == ui::VKEY_BACK)
+ model_->Backspace();
+ else if (shift && model_->HasSelection() && readable)
+ Cut();
+ else
+ model_->Delete();
+
+ // Consume backspace and delete keys even if the edit did nothing. This
+ // prevents potential unintended side-effects of further event handling.
+ text_changed = true;
+ break;
+ case ui::VKEY_INSERT:
+ if (control && !shift && readable)
+ Copy();
+ else if (shift && !control && editable)
+ cursor_changed = text_changed = Paste();
+ break;
+ default:
+ break;
+ }
+
+ // We must have input method in order to support text input.
+ DCHECK(GetInputMethod());
+ UpdateAfterChange(text_changed, cursor_changed);
+ OnAfterUserAction();
+ return (text_changed || cursor_changed);
+ }
+ return false;
+}
+
+bool Textfield::OnMousePressed(const ui::MouseEvent& event) {
+ OnBeforeUserAction();
+ TrackMouseClicks(event);
+
+ if (!controller_ || !controller_->HandleMouseEvent(this, event)) {
+ if (event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton())
+ RequestFocus();
+
+ if (event.IsOnlyLeftMouseButton()) {
+ initiating_drag_ = false;
+ bool can_drag = true;
+
+ switch (aggregated_clicks_) {
+ case 0:
+ if (can_drag &&
+ GetRenderText()->IsPointInSelection(event.location())) {
+ initiating_drag_ = true;
+ } else {
+ MoveCursorTo(event.location(), event.IsShiftDown());
+ }
+ break;
+ case 1:
+ MoveCursorTo(event.location(), false);
+ model_->SelectWord();
+ double_click_word_ = GetRenderText()->selection();
+ OnCaretBoundsChanged();
+ break;
+ case 2:
+ model_->SelectAll(false);
+ OnCaretBoundsChanged();
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+ SchedulePaint();
+ }
-bool Textfield::OnKeyReleased(const ui::KeyEvent& e) {
- return textfield_view_ && textfield_view_->HandleKeyReleased(e);
+ OnAfterUserAction();
+ touch_selection_controller_.reset();
+ return true;
}
-bool Textfield::OnMouseDragged(const ui::MouseEvent& e) {
- if (!e.IsOnlyRightMouseButton())
- return View::OnMouseDragged(e);
+bool Textfield::OnMouseDragged(const ui::MouseEvent& event) {
+ // Don't adjust the cursor on a potential drag and drop, or if the mouse
+ // movement from the last mouse click does not exceed the drag threshold.
+ if (initiating_drag_ || !event.IsOnlyLeftMouseButton() ||
+ !ExceededDragThreshold(event.location() - last_click_location_)) {
+ 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();
+ }
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();
+ initiating_drag_ = false;
+ OnAfterUserAction();
+}
+
void Textfield::OnFocus() {
- if (textfield_view_)
- textfield_view_->HandleFocus();
+ GetRenderText()->set_focused(true);
+ is_cursor_visible_ = true;
+ SchedulePaint();
+ GetInputMethod()->OnFocus();
+ OnCaretBoundsChanged();
+
+ const size_t caret_blink_ms = Textfield::GetCaretBlinkMs();
+ if (caret_blink_ms != 0) {
+ cursor_repaint_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(caret_blink_ms), this,
+ &Textfield::UpdateCursor);
+ }
+
View::OnFocus();
SchedulePaint();
}
void Textfield::OnBlur() {
- if (textfield_view_)
- textfield_view_->HandleBlur();
+ GetRenderText()->set_focused(false);
+ GetInputMethod()->OnBlur();
+ cursor_repaint_timer_.Stop();
+ if (is_cursor_visible_) {
+ is_cursor_visible_ = false;
+ RepaintCursor();
+ }
+
+ touch_selection_controller_.reset();
// Border typically draws focus indicator.
SchedulePaint();
@@ -496,9 +644,9 @@ void Textfield::GetAccessibleState(ui::AccessibleViewState* state) {
state->state |= ui::AccessibilityTypes::STATE_READONLY;
if (IsObscured())
state->state |= ui::AccessibilityTypes::STATE_PROTECTED;
- state->value = text_;
+ state->value = text();
- const gfx::Range range = textfield_view_->GetSelectedRange();
+ const gfx::Range range = GetSelectedRange();
state->selection_start = range.start();
state->selection_end = range.end();
@@ -510,43 +658,679 @@ void Textfield::GetAccessibleState(ui::AccessibleViewState* state) {
}
ui::TextInputClient* Textfield::GetTextInputClient() {
- return textfield_view_ ? textfield_view_->GetTextInputClient() : NULL;
+ return read_only_ ? NULL : this;
}
gfx::Point Textfield::GetKeyboardContextMenuLocation() {
- return textfield_view_ ? textfield_view_->GetContextMenuLocation() :
- View::GetKeyboardContextMenuLocation();
+ return GetCaretBounds().bottom_right();
+}
+
+void Textfield::OnNativeThemeChanged(const ui::NativeTheme* theme) {
+ UpdateColorsFromTheme(theme);
}
void Textfield::OnEnabledChanged() {
View::OnEnabledChanged();
- if (textfield_view_)
- textfield_view_->UpdateEnabled();
-}
-
-void Textfield::ViewHierarchyChanged(
- const ViewHierarchyChangedDetails& details) {
- if (details.is_add && !textfield_view_ && GetWidget()) {
- // The textfield view's lifetime is managed by the view hierarchy.
- textfield_view_ = new NativeTextfieldViews(this);
- AddChildViewAt(textfield_view_, 0);
- Layout();
- UpdateAllProperties();
- }
+ if (GetInputMethod())
+ GetInputMethod()->OnTextInputTypeChanged(this);
+ SchedulePaint();
}
const char* Textfield::GetClassName() const {
return kViewClassName;
}
+gfx::NativeCursor Textfield::GetCursor(const ui::MouseEvent& event) {
+ bool in_selection = GetRenderText()->IsPointInSelection(event.location());
+ bool drag_event = event.type() == ui::ET_MOUSE_DRAGGED;
+ bool text_cursor = !initiating_drag_ && (drag_event || !in_selection);
+#if defined(USE_AURA)
+ return text_cursor ? ui::kCursorIBeam : ui::kCursorNull;
+#elif defined(OS_WIN)
+ static HCURSOR ibeam = LoadCursor(NULL, IDC_IBEAM);
+ static HCURSOR arrow = LoadCursor(NULL, IDC_ARROW);
+ return text_cursor ? ibeam : arrow;
+#endif
+}
+
+void Textfield::OnGestureEvent(ui::GestureEvent* event) {
+ switch (event->type()) {
+ case ui::ET_GESTURE_TAP_DOWN:
+ OnBeforeUserAction();
+ 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();
+ OnAfterUserAction();
+ event->SetHandled();
+ break;
+ case ui::ET_GESTURE_SCROLL_UPDATE:
+ OnBeforeUserAction();
+ if (MoveCursorTo(event->location(), true))
+ SchedulePaint();
+ OnAfterUserAction();
+ event->SetHandled();
+ break;
+ case ui::ET_GESTURE_SCROLL_END:
+ case ui::ET_SCROLL_FLING_START:
+ CreateTouchSelectionControllerAndNotifyIt();
+ event->SetHandled();
+ break;
+ case ui::ET_GESTURE_TAP:
+ if (event->details().tap_count() == 1) {
+ CreateTouchSelectionControllerAndNotifyIt();
+ } else {
+ OnBeforeUserAction();
+ SelectAll(false);
+ OnAfterUserAction();
+ event->SetHandled();
+ }
+#if defined(OS_WIN) && defined(USE_AURA)
+ if (!read_only())
+ base::win::DisplayVirtualKeyboard();
+#endif
+ break;
+ case ui::ET_GESTURE_LONG_PRESS:
+ // If long press happens outside selection, select word and show context
+ // menu (If touch selection is enabled, context menu is shown by the
+ // |touch_selection_controller_|, hence we mark the event handled.
+ // Otherwise, the regular context menu will be shown by views).
+ // If long press happens in selected text and touch drag drop is enabled,
+ // we will turn off touch selection (if one exists) and let views do drag
+ // drop.
+ if (!GetRenderText()->IsPointInSelection(event->location())) {
+ OnBeforeUserAction();
+ model_->SelectWord();
+ touch_selection_controller_.reset(
+ ui::TouchSelectionController::create(this));
+ OnCaretBoundsChanged();
+ SchedulePaint();
+ OnAfterUserAction();
+ if (touch_selection_controller_)
+ event->SetHandled();
+ } else if (switches::IsTouchDragDropEnabled()) {
+ initiating_drag_ = true;
+ touch_selection_controller_.reset();
+ } else {
+ if (!touch_selection_controller_)
+ CreateTouchSelectionControllerAndNotifyIt();
+ if (touch_selection_controller_)
+ event->SetHandled();
+ }
+ return;
+ case ui::ET_GESTURE_LONG_TAP:
+ if (!touch_selection_controller_)
+ CreateTouchSelectionControllerAndNotifyIt();
+
+ // If touch selection is enabled, the context menu on long tap will be
+ // shown by the |touch_selection_controller_|, hence we mark the event
+ // handled so views does not try to show context menu on it.
+ if (touch_selection_controller_)
+ event->SetHandled();
+ break;
+ default:
+ return;
+ }
+}
+
+bool Textfield::GetDropFormats(
+ int* formats,
+ std::set<OSExchangeData::CustomFormat>* custom_formats) {
+ if (!enabled() || read_only())
+ return false;
+ // TODO(msw): Can we support URL, FILENAME, etc.?
+ *formats = ui::OSExchangeData::STRING;
+ if (controller_)
+ controller_->AppendDropFormats(formats, custom_formats);
+ return true;
+}
+
+bool Textfield::CanDrop(const OSExchangeData& data) {
+ int formats;
+ std::set<OSExchangeData::CustomFormat> custom_formats;
+ GetDropFormats(&formats, &custom_formats);
+ return enabled() && !read_only() &&
+ data.HasAnyFormat(formats, custom_formats);
+}
+
+int Textfield::OnDragUpdated(const ui::DropTargetEvent& event) {
+ DCHECK(CanDrop(event.data()));
+ gfx::RenderText* render_text = GetRenderText();
+ const gfx::Range& selection = render_text->selection();
+ drop_cursor_position_ = render_text->FindCursorPosition(event.location());
+ bool in_selection = !selection.is_empty() &&
+ selection.Contains(gfx::Range(drop_cursor_position_.caret_pos()));
+ is_drop_cursor_visible_ = !in_selection;
+ // TODO(msw): Pan over text when the user drags to the visible text edge.
+ OnCaretBoundsChanged();
+ SchedulePaint();
+
+ if (initiating_drag_) {
+ if (in_selection)
+ return ui::DragDropTypes::DRAG_NONE;
+ return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY :
+ ui::DragDropTypes::DRAG_MOVE;
+ }
+ return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE;
+}
+
+void Textfield::OnDragExited() {
+ is_drop_cursor_visible_ = false;
+ SchedulePaint();
+}
+
+int Textfield::OnPerformDrop(const ui::DropTargetEvent& event) {
+ DCHECK(CanDrop(event.data()));
+ is_drop_cursor_visible_ = false;
+
+ if (controller_) {
+ int drag_operation = controller_->OnDrop(event.data());
+ if (drag_operation != ui::DragDropTypes::DRAG_NONE)
+ return drag_operation;
+ }
+
+ gfx::RenderText* render_text = GetRenderText();
+ DCHECK(!initiating_drag_ ||
+ !render_text->IsPointInSelection(event.location()));
+ OnBeforeUserAction();
+ skip_input_method_cancel_composition_ = true;
+
+ gfx::SelectionModel drop_destination_model =
+ render_text->FindCursorPosition(event.location());
+ base::string16 new_text;
+ event.data().GetString(&new_text);
+ new_text = GetTextForDisplay(new_text);
+
+ // Delete the current selection for a drag and drop within this view.
+ const bool move = initiating_drag_ && !event.IsControlDown() &&
+ event.source_operations() & ui::DragDropTypes::DRAG_MOVE;
+ if (move) {
+ // Adjust the drop destination if it is on or after the current selection.
+ size_t pos = drop_destination_model.caret_pos();
+ pos -= render_text->selection().Intersect(gfx::Range(0, pos)).length();
+ model_->DeleteSelectionAndInsertTextAt(new_text, pos);
+ } else {
+ model_->MoveCursorTo(drop_destination_model);
+ // Drop always inserts text even if the textfield is not in insert mode.
+ model_->InsertText(new_text);
+ }
+ skip_input_method_cancel_composition_ = false;
+ UpdateAfterChange(true, true);
+ OnAfterUserAction();
+ return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY;
+}
+
+void Textfield::OnDragDone() {
+ initiating_drag_ = false;
+ is_drop_cursor_visible_ = false;
+}
+
+void Textfield::OnBoundsChanged(const gfx::Rect& previous_bounds) {
+ GetRenderText()->SetDisplayRect(GetContentsBounds());
+ OnCaretBoundsChanged();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Textfield, TextfieldViewsModel::Delegate overrides:
+
+void Textfield::OnCompositionTextConfirmedOrCleared() {
+ if (!skip_input_method_cancel_composition_)
+ GetInputMethod()->CancelComposition(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Textfield, ContextMenuController overrides:
+
+void Textfield::ShowContextMenuForView(
+ View* source,
+ const gfx::Point& point,
+ ui::MenuSourceType source_type) {
+ UpdateContextMenu();
+ if (context_menu_runner_->RunMenuAt(GetWidget(), NULL,
+ gfx::Rect(point, gfx::Size()), views::MenuItemView::TOPLEFT,
+ source_type,
+ MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU) ==
+ MenuRunner::MENU_DELETED)
+ return;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Textfield, views::DragController overrides:
+
+void Textfield::WriteDragDataForView(views::View* sender,
+ const gfx::Point& press_pt,
+ OSExchangeData* data) {
+ DCHECK_NE(ui::DragDropTypes::DRAG_NONE,
+ GetDragOperationsForView(sender, press_pt));
+ data->SetString(model_->GetSelectedText());
+ scoped_ptr<gfx::Canvas> canvas(
+ views::GetCanvasForDragImage(GetWidget(), size()));
+ GetRenderText()->DrawSelectedTextForDrag(canvas.get());
+ drag_utils::SetDragImageOnDataObject(*canvas, size(),
+ press_pt.OffsetFromOrigin(),
+ data);
+ if (controller_)
+ controller_->OnWriteDragData(data);
+}
+
+int Textfield::GetDragOperationsForView(views::View* sender,
+ const gfx::Point& p) {
+ int drag_operations = ui::DragDropTypes::DRAG_COPY;
+ if (!enabled() || IsObscured() || !GetRenderText()->IsPointInSelection(p))
+ drag_operations = ui::DragDropTypes::DRAG_NONE;
+ else if (sender == this && !read_only())
+ drag_operations =
+ ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY;
+ if (controller_)
+ controller_->OnGetDragOperationsForTextfield(&drag_operations);
+ return drag_operations;
+}
+
+bool Textfield::CanStartDragForView(View* sender,
+ const gfx::Point& press_pt,
+ const gfx::Point& p) {
+ return initiating_drag_ && GetRenderText()->IsPointInSelection(press_pt);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Textfield, ui::TouchEditable overrides:
+
+void Textfield::SelectRect(const gfx::Point& start, const gfx::Point& end) {
+ if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
+ return;
+
+ gfx::SelectionModel start_caret = GetRenderText()->FindCursorPosition(start);
+ gfx::SelectionModel end_caret = GetRenderText()->FindCursorPosition(end);
+ gfx::SelectionModel selection(
+ gfx::Range(start_caret.caret_pos(), end_caret.caret_pos()),
+ end_caret.caret_affinity());
+
+ OnBeforeUserAction();
+ model_->SelectSelectionModel(selection);
+ OnCaretBoundsChanged();
+ SchedulePaint();
+ OnAfterUserAction();
+}
+
+void Textfield::MoveCaretTo(const gfx::Point& point) {
+ SelectRect(point, point);
+}
+
+void Textfield::GetSelectionEndPoints(gfx::Rect* p1, gfx::Rect* p2) {
+ gfx::RenderText* render_text = GetRenderText();
+ const gfx::SelectionModel& sel = render_text->selection_model();
+ gfx::SelectionModel start_sel =
+ render_text->GetSelectionModelForSelectionStart();
+ *p1 = render_text->GetCursorBounds(start_sel, true);
+ *p2 = render_text->GetCursorBounds(sel, true);
+}
+
+gfx::Rect Textfield::GetBounds() {
+ return bounds();
+}
+
+gfx::NativeView Textfield::GetNativeView() const {
+ return GetWidget()->GetNativeView();
+}
+
+void Textfield::ConvertPointToScreen(gfx::Point* point) {
+ View::ConvertPointToScreen(this, point);
+}
+
+void Textfield::ConvertPointFromScreen(gfx::Point* point) {
+ View::ConvertPointFromScreen(this, point);
+}
+
+bool Textfield::DrawsHandles() {
+ return false;
+}
+
+void Textfield::OpenContextMenu(const gfx::Point& anchor) {
+ touch_selection_controller_.reset();
+ ShowContextMenu(anchor, ui::MENU_SOURCE_TOUCH_EDIT_MENU);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Textfield, ui::SimpleMenuModel::Delegate overrides:
+
+bool Textfield::IsCommandIdChecked(int command_id) const {
+ return true;
+}
+
+bool Textfield::IsCommandIdEnabled(int command_id) const {
+ base::string16 result;
+ bool editable = !read_only();
+ switch (command_id) {
+ case IDS_APP_UNDO:
+ return editable && model_->CanUndo();
+ case IDS_APP_CUT:
+ return editable && model_->HasSelection() && !IsObscured();
+ case IDS_APP_COPY:
+ return model_->HasSelection() && !IsObscured();
+ case IDS_APP_PASTE:
+ ui::Clipboard::GetForCurrentThread()->ReadText(
+ ui::CLIPBOARD_TYPE_COPY_PASTE, &result);
+ return editable && !result.empty();
+ case IDS_APP_DELETE:
+ return editable && model_->HasSelection();
+ case IDS_APP_SELECT_ALL:
+ return !text().empty();
+ default:
+ return false;
+ }
+}
+
+bool Textfield::GetAcceleratorForCommandId(int command_id,
+ ui::Accelerator* accelerator) {
+ return false;
+}
+
+void Textfield::ExecuteCommand(int command_id, int event_flags) {
+ touch_selection_controller_.reset();
+ if (!IsCommandIdEnabled(command_id))
+ return;
+
+ bool text_changed = false;
+ 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;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Textfield, ui::TextInputClient overrides:
+
+void Textfield::SetCompositionText(const ui::CompositionText& composition) {
+ if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
+ return;
+
+ OnBeforeUserAction();
+ skip_input_method_cancel_composition_ = true;
+ model_->SetCompositionText(composition);
+ skip_input_method_cancel_composition_ = false;
+ UpdateAfterChange(true, true);
+ OnAfterUserAction();
+}
+
+void Textfield::ConfirmCompositionText() {
+ if (!model_->HasCompositionText())
+ return;
+
+ OnBeforeUserAction();
+ skip_input_method_cancel_composition_ = true;
+ model_->ConfirmCompositionText();
+ skip_input_method_cancel_composition_ = false;
+ UpdateAfterChange(true, true);
+ OnAfterUserAction();
+}
+
+void Textfield::ClearCompositionText() {
+ if (!model_->HasCompositionText())
+ return;
+
+ OnBeforeUserAction();
+ skip_input_method_cancel_composition_ = true;
+ model_->CancelCompositionText();
+ skip_input_method_cancel_composition_ = false;
+ UpdateAfterChange(true, true);
+ OnAfterUserAction();
+}
+
+void Textfield::InsertText(const base::string16& new_text) {
+ // TODO(suzhe): Filter invalid characters.
+ if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || new_text.empty())
+ return;
+
+ OnBeforeUserAction();
+ skip_input_method_cancel_composition_ = true;
+ if (GetRenderText()->insert_mode())
+ model_->InsertText(GetTextForDisplay(new_text));
+ else
+ model_->ReplaceText(GetTextForDisplay(new_text));
+ skip_input_method_cancel_composition_ = false;
+ UpdateAfterChange(true, true);
+ OnAfterUserAction();
+}
+
+void Textfield::InsertChar(base::char16 ch, int flags) {
+ // Filter out all control characters, including tab and new line characters,
+ // and all characters with Alt modifier. But allow characters with the AltGr
+ // modifier. On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a
+ // different flag that we don't care about.
+ const bool should_insert_char = ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) &&
+ (flags & ~(ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN)) != ui::EF_ALT_DOWN;
+ if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || !should_insert_char)
+ return;
+
+ OnBeforeUserAction();
+ skip_input_method_cancel_composition_ = true;
+ if (GetRenderText()->insert_mode())
+ model_->InsertChar(ch);
+ else
+ model_->ReplaceChar(ch);
+ skip_input_method_cancel_composition_ = false;
+
+ model_->SetText(GetTextForDisplay(text()));
+
+ UpdateAfterChange(true, true);
+ OnAfterUserAction();
+
+ if (IsObscured() && obscured_reveal_duration_ != base::TimeDelta()) {
+ const size_t change_offset = model_->GetCursorPosition();
+ DCHECK_GT(change_offset, 0u);
+ RevealObscuredChar(change_offset - 1, obscured_reveal_duration_);
+ }
+}
+
+gfx::NativeWindow Textfield::GetAttachedWindow() const {
+ // Imagine the following hierarchy.
+ // [NativeWidget A] - FocusManager
+ // [View]
+ // [NativeWidget B]
+ // [View]
+ // [View X]
+ // An important thing is that [NativeWidget A] owns Win32 input focus even
+ // when [View X] is logically focused by FocusManager. As a result, an Win32
+ // IME may want to interact with the native view of [NativeWidget A] rather
+ // than that of [NativeWidget B]. This is why we need to call
+ // GetTopLevelWidget() here.
+ return GetWidget()->GetTopLevelWidget()->GetNativeView();
+}
+
+ui::TextInputType Textfield::GetTextInputType() const {
+ if (read_only() || !enabled())
+ return ui::TEXT_INPUT_TYPE_NONE;
+ return text_input_type_;
+}
+
+ui::TextInputMode Textfield::GetTextInputMode() const {
+ return ui::TEXT_INPUT_MODE_DEFAULT;
+}
+
+bool Textfield::CanComposeInline() const {
+ return true;
+}
+
+gfx::Rect Textfield::GetCaretBounds() const {
+ gfx::Rect rect = GetRenderText()->GetUpdatedCursorBounds();
+ ConvertRectToScreen(this, &rect);
+ return rect;
+}
+
+bool Textfield::GetCompositionCharacterBounds(uint32 index,
+ gfx::Rect* rect) const {
+ DCHECK(rect);
+ if (!HasCompositionText())
+ return false;
+ gfx::RenderText* render_text = GetRenderText();
+ const gfx::Range& composition_range = render_text->GetCompositionRange();
+ DCHECK(!composition_range.is_empty());
+
+ size_t text_index = composition_range.start() + index;
+ if (composition_range.end() <= text_index)
+ return false;
+ if (!render_text->IsCursorablePosition(text_index)) {
+ text_index = render_text->IndexOfAdjacentGrapheme(
+ text_index, gfx::CURSOR_BACKWARD);
+ }
+ if (text_index < composition_range.start())
+ return false;
+ const gfx::SelectionModel caret(text_index, gfx::CURSOR_BACKWARD);
+ *rect = render_text->GetCursorBounds(caret, false);
+ ConvertRectToScreen(this, rect);
+ return true;
+}
+
+bool Textfield::HasCompositionText() const {
+ return model_->HasCompositionText();
+}
+
+bool Textfield::GetTextRange(gfx::Range* range) const {
+ if (!ImeEditingAllowed())
+ return false;
+
+ model_->GetTextRange(range);
+ return true;
+}
+
+bool Textfield::GetCompositionTextRange(gfx::Range* range) const {
+ if (!ImeEditingAllowed())
+ return false;
+
+ model_->GetCompositionTextRange(range);
+ return true;
+}
+
+bool Textfield::GetSelectionRange(gfx::Range* range) const {
+ if (!ImeEditingAllowed())
+ return false;
+ *range = GetRenderText()->selection();
+ return true;
+}
+
+bool Textfield::SetSelectionRange(const gfx::Range& range) {
+ if (!ImeEditingAllowed() || !range.IsValid())
+ return false;
+
+ OnBeforeUserAction();
+ SelectRange(range);
+ OnAfterUserAction();
+ return true;
+}
+
+bool Textfield::DeleteRange(const gfx::Range& range) {
+ if (!ImeEditingAllowed() || range.is_empty())
+ return false;
+
+ OnBeforeUserAction();
+ model_->SelectRange(range);
+ if (model_->HasSelection()) {
+ model_->DeleteSelection();
+ UpdateAfterChange(true, true);
+ }
+ OnAfterUserAction();
+ return true;
+}
+
+bool Textfield::GetTextFromRange(const gfx::Range& range,
+ base::string16* range_text) const {
+ if (!ImeEditingAllowed() || !range.IsValid())
+ return false;
+
+ gfx::Range text_range;
+ if (!GetTextRange(&text_range) || !text_range.Contains(range))
+ return false;
+
+ *range_text = model_->GetTextFromRange(range);
+ return true;
+}
+
+void Textfield::OnInputMethodChanged() {}
+
+bool Textfield::ChangeTextDirectionAndLayoutAlignment(
+ base::i18n::TextDirection direction) {
+ // Restore text directionality mode when the indicated direction matches the
+ // current forced mode; otherwise, force the mode indicated. This helps users
+ // manage BiDi text layout without getting stuck in forced LTR or RTL modes.
+ const gfx::DirectionalityMode mode = direction == base::i18n::RIGHT_TO_LEFT ?
+ gfx::DIRECTIONALITY_FORCE_RTL : gfx::DIRECTIONALITY_FORCE_LTR;
+ if (mode == GetRenderText()->directionality_mode())
+ GetRenderText()->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT);
+ else
+ GetRenderText()->SetDirectionalityMode(mode);
+ SchedulePaint();
+ return true;
+}
+
+void Textfield::ExtendSelectionAndDelete(size_t before, size_t after) {
+ gfx::Range range = GetRenderText()->selection();
+ DCHECK_GE(range.start(), before);
+
+ range.set_start(range.start() - before);
+ range.set_end(range.end() + after);
+ gfx::Range text_range;
+ if (GetTextRange(&text_range) && text_range.Contains(range))
+ DeleteRange(range);
+}
+
+void Textfield::EnsureCaretInRect(const gfx::Rect& rect) {}
+
+void Textfield::OnCandidateWindowShown() {}
+
+void Textfield::OnCandidateWindowUpdated() {}
+
+void Textfield::OnCandidateWindowHidden() {}
+
+////////////////////////////////////////////////////////////////////////////////
+// Textfield, protected:
+
+gfx::RenderText* Textfield::GetRenderText() const {
+ return model_->render_text();
+}
+
////////////////////////////////////////////////////////////////////////////////
// Textfield, private:
-gfx::Insets Textfield::GetTextInsets() const {
- gfx::Insets insets = GetInsets();
- if (draw_border_ && textfield_view_)
- insets += textfield_view_->GetInsets();
- return insets;
+base::string16 Textfield::GetTextForDisplay(const base::string16& raw) {
+ return style_ & Textfield::STYLE_LOWERCASE ? base::i18n::ToLower(raw) : raw;
}
void Textfield::AccessibilitySetValue(const base::string16& new_value) {
@@ -556,4 +1340,200 @@ void Textfield::AccessibilitySetValue(const base::string16& new_value) {
}
}
+void Textfield::UpdateBackgroundColor() {
+ const SkColor color = GetBackgroundColor();
+ set_background(Background::CreateSolidBackground(color));
+ GetRenderText()->set_background_is_transparent(SkColorGetA(color) != 0xFF);
+ SchedulePaint();
+}
+
+void Textfield::UpdateColorsFromTheme(const ui::NativeTheme* theme) {
+ gfx::RenderText* render_text = GetRenderText();
+ render_text->SetColor(GetTextColor());
+ UpdateBackgroundColor();
+ render_text->set_cursor_color(GetTextColor());
+ render_text->set_selection_color(theme->GetSystemColor(
+ ui::NativeTheme::kColorId_TextfieldSelectionColor));
+ render_text->set_selection_background_focused_color(theme->GetSystemColor(
+ ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused));
+}
+
+void Textfield::UpdateAfterChange(bool text_changed, bool cursor_changed) {
+ if (text_changed) {
+ if (controller_)
+ controller_->ContentsChanged(this, text());
+ NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_TEXT_CHANGED, true);
+ }
+ if (cursor_changed) {
+ is_cursor_visible_ = true;
+ RepaintCursor();
+ if (cursor_repaint_timer_.IsRunning())
+ cursor_repaint_timer_.Reset();
+ if (!text_changed) {
+ // TEXT_CHANGED implies SELECTION_CHANGED, so we only need to fire
+ // this if only the selection changed.
+ NotifyAccessibilityEvent(
+ ui::AccessibilityTypes::EVENT_SELECTION_CHANGED, true);
+ }
+ }
+ if (text_changed || cursor_changed) {
+ OnCaretBoundsChanged();
+ SchedulePaint();
+ }
+}
+
+void Textfield::UpdateCursor() {
+ const size_t caret_blink_ms = Textfield::GetCaretBlinkMs();
+ is_cursor_visible_ = !is_cursor_visible_ || (caret_blink_ms == 0);
+ RepaintCursor();
+}
+
+void Textfield::RepaintCursor() {
+ gfx::Rect r(GetRenderText()->GetUpdatedCursorBounds());
+ r.Inset(-1, -1, -1, -1);
+ SchedulePaintInRect(r);
+}
+
+void Textfield::PaintTextAndCursor(gfx::Canvas* canvas) {
+ TRACE_EVENT0("views", "Textfield::PaintTextAndCursor");
+ canvas->Save();
+ gfx::RenderText* render_text = GetRenderText();
+ render_text->set_cursor_visible(!is_drop_cursor_visible_ &&
+ is_cursor_visible_ && !model_->HasSelection());
+ // Draw the text, cursor, and selection.
+ render_text->Draw(canvas);
+
+ // Draw the detached drop cursor that marks where the text will be dropped.
+ if (is_drop_cursor_visible_)
+ render_text->DrawCursor(canvas, drop_cursor_position_);
+
+ // Draw placeholder text if needed.
+ if (text().empty() && !GetPlaceholderText().empty()) {
+ canvas->DrawStringRect(GetPlaceholderText(), GetFontList(),
+ placeholder_text_color(), render_text->display_rect());
+ }
+ canvas->Restore();
+}
+
+bool Textfield::MoveCursorTo(const gfx::Point& point, bool select) {
+ if (!model_->MoveCursorTo(point, select))
+ return false;
+ OnCaretBoundsChanged();
+ return true;
+}
+
+void Textfield::OnCaretBoundsChanged() {
+ if (GetInputMethod())
+ GetInputMethod()->OnCaretBoundsChanged(this);
+ if (touch_selection_controller_)
+ touch_selection_controller_->SelectionChanged();
+}
+
+void Textfield::OnBeforeUserAction() {
+ if (controller_)
+ controller_->OnBeforeUserAction(this);
+}
+
+void Textfield::OnAfterUserAction() {
+ if (controller_)
+ controller_->OnAfterUserAction(this);
+}
+
+bool Textfield::Cut() {
+ if (!read_only() && !IsObscured() && model_->Cut()) {
+ if (controller_)
+ controller_->OnAfterCutOrCopy();
+ return true;
+ }
+ return false;
+}
+
+bool Textfield::Copy() {
+ if (!IsObscured() && model_->Copy()) {
+ if (controller_)
+ controller_->OnAfterCutOrCopy();
+ return true;
+ }
+ return false;
+}
+
+bool Textfield::Paste() {
+ if (read_only())
+ return false;
+
+ const base::string16 original_text = text();
+ if (model_->Paste()) {
+ // As Paste is handled in model_->Paste(), the RenderText may contain
+ // upper case characters. This is not consistent with other places
+ // which keeps RenderText only containing lower case characters.
+ base::string16 new_text = GetTextForDisplay(text());
+ model_->SetText(new_text);
+ if (controller_)
+ controller_->OnAfterPaste();
+ return true;
+ }
+ return false;
+}
+
+void Textfield::UpdateContextMenu() {
+ if (!context_menu_contents_.get()) {
+ context_menu_contents_.reset(new ui::SimpleMenuModel(this));
+ context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO);
+ context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
+ 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(ui::NORMAL_SEPARATOR);
+ context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL,
+ IDS_APP_SELECT_ALL);
+ if (controller_)
+ controller_->UpdateContextMenu(context_menu_contents_.get());
+ context_menu_runner_.reset(new MenuRunner(context_menu_contents_.get()));
+ }
+}
+
+void Textfield::TrackMouseClicks(const ui::MouseEvent& event) {
+ if (event.IsOnlyLeftMouseButton()) {
+ base::TimeDelta time_delta = event.time_stamp() - last_click_time_;
+ if (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();
+ return (t != ui::TEXT_INPUT_TYPE_NONE && t != ui::TEXT_INPUT_TYPE_PASSWORD);
+}
+
+void Textfield::RevealObscuredChar(int index, const base::TimeDelta& duration) {
+ GetRenderText()->SetObscuredRevealIndex(index);
+ SchedulePaint();
+
+ if (index != -1) {
+ obscured_reveal_timer_.Start(FROM_HERE, duration,
+ base::Bind(&Textfield::RevealObscuredChar, base::Unretained(this),
+ -1, base::TimeDelta()));
+ }
+}
+
+void Textfield::CreateTouchSelectionControllerAndNotifyIt() {
+ if (!touch_selection_controller_) {
+ touch_selection_controller_.reset(
+ ui::TouchSelectionController::create(this));
+ }
+ if (touch_selection_controller_)
+ touch_selection_controller_->SelectionChanged();
+}
+
} // namespace views
« no previous file with comments | « ui/views/controls/textfield/textfield.h ('k') | ui/views/controls/textfield/textfield_controller.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698