Index: views/controls/textfield/native_textfield_views.cc |
diff --git a/views/controls/textfield/native_textfield_views.cc b/views/controls/textfield/native_textfield_views.cc |
index e1b8ccc248122b6ec708036253a0d458e5c3185c..73b95f3710e7f7bd57159a5b5851b5ca237571c7 100644 |
--- a/views/controls/textfield/native_textfield_views.cc |
+++ b/views/controls/textfield/native_textfield_views.cc |
@@ -23,6 +23,7 @@ |
#include "views/controls/textfield/textfield_controller.h" |
#include "views/controls/textfield/textfield_views_model.h" |
#include "views/events/event.h" |
+#include "views/ime/input_method.h" |
#include "views/metrics.h" |
#include "views/views_delegate.h" |
@@ -61,11 +62,12 @@ const char NativeTextfieldViews::kViewClassName[] = |
NativeTextfieldViews::NativeTextfieldViews(Textfield* parent) |
: textfield_(parent), |
- model_(new TextfieldViewsModel()), |
+ ALLOW_THIS_IN_INITIALIZER_LIST(model_(new TextfieldViewsModel(this))), |
text_border_(new TextfieldBorder()), |
text_offset_(0), |
insert_(true), |
is_cursor_visible_(false), |
+ skip_input_method_cancel_composition_(false), |
ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)), |
last_mouse_press_time_(base::Time::FromInternalValue(0)), |
click_state_(NONE) { |
@@ -160,14 +162,9 @@ string16 NativeTextfieldViews::GetText() const { |
} |
void NativeTextfieldViews::UpdateText() { |
- bool changed = model_->SetText(textfield_->text()); |
+ model_->SetText(textfield_->text()); |
UpdateCursorBoundsAndTextOffset(); |
SchedulePaint(); |
- if (changed) { |
- TextfieldController* controller = textfield_->GetController(); |
- if (controller) |
- controller->ContentsChanged(textfield_, GetText()); |
- } |
} |
void NativeTextfieldViews::AppendText(const string16& text) { |
@@ -176,10 +173,6 @@ void NativeTextfieldViews::AppendText(const string16& text) { |
model_->Append(text); |
UpdateCursorBoundsAndTextOffset(); |
SchedulePaint(); |
- |
- TextfieldController* controller = textfield_->GetController(); |
- if (controller) |
- controller->ContentsChanged(textfield_, GetText()); |
} |
string16 NativeTextfieldViews::GetSelectedText() const { |
@@ -220,6 +213,7 @@ void NativeTextfieldViews::UpdateBackgroundColor() { |
void NativeTextfieldViews::UpdateReadOnly() { |
SchedulePaint(); |
+ OnTextInputTypeChanged(); |
} |
void NativeTextfieldViews::UpdateFont() { |
@@ -230,11 +224,13 @@ void NativeTextfieldViews::UpdateIsPassword() { |
model_->set_is_password(textfield_->IsPassword()); |
UpdateCursorBoundsAndTextOffset(); |
SchedulePaint(); |
+ OnTextInputTypeChanged(); |
} |
void NativeTextfieldViews::UpdateEnabled() { |
SetEnabled(textfield_->IsEnabled()); |
SchedulePaint(); |
+ OnTextInputTypeChanged(); |
} |
gfx::Insets NativeTextfieldViews::CalculateInsets() { |
@@ -275,7 +271,7 @@ gfx::NativeView NativeTextfieldViews::GetTestingHandle() const { |
} |
bool NativeTextfieldViews::IsIMEComposing() const { |
- return false; |
+ return model_->HasCompositionText(); |
} |
void NativeTextfieldViews::GetSelectedRange(ui::Range* range) const { |
@@ -307,6 +303,7 @@ bool NativeTextfieldViews::HandleKeyReleased(const views::KeyEvent& e) { |
void NativeTextfieldViews::HandleFocus() { |
is_cursor_visible_ = true; |
SchedulePaint(); |
+ OnCaretBoundsChanged(); |
// Start blinking cursor. |
MessageLoop::current()->PostDelayedTask( |
FROM_HERE, |
@@ -323,6 +320,10 @@ void NativeTextfieldViews::HandleBlur() { |
} |
} |
+TextInputClient* NativeTextfieldViews::GetTextInputClient() { |
+ return textfield_->read_only() ? NULL : this; |
+} |
+ |
///////////////////////////////////////////////////////////////// |
// NativeTextfieldViews, ui::SimpleMenuModel::Delegate overrides: |
@@ -412,7 +413,174 @@ void NativeTextfieldViews::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
/////////////////////////////////////////////////////////////////////////////// |
-// NativeTextfieldViews private: |
+// NativeTextfieldViews, TextInputClient implementation, private: |
+ |
+void NativeTextfieldViews::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 NativeTextfieldViews::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 NativeTextfieldViews::ClearCompositionText() { |
+ if (!model_->HasCompositionText()) |
+ return; |
+ |
+ OnBeforeUserAction(); |
+ skip_input_method_cancel_composition_ = true; |
+ model_->ClearCompositionText(); |
+ skip_input_method_cancel_composition_ = false; |
+ UpdateAfterChange(true, true); |
+ OnAfterUserAction(); |
+} |
+ |
+void NativeTextfieldViews::InsertText(const string16& text) { |
+ // TODO(suzhe): Filter invalid characters. |
+ if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || text.empty()) |
+ return; |
+ |
+ OnBeforeUserAction(); |
+ skip_input_method_cancel_composition_ = true; |
+ if (insert_) |
+ model_->InsertText(text); |
+ else |
+ model_->ReplaceText(text); |
+ skip_input_method_cancel_composition_ = false; |
+ UpdateAfterChange(true, true); |
+ OnAfterUserAction(); |
+} |
+ |
+void NativeTextfieldViews::InsertChar(char16 ch, int flags) { |
+ if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || |
+ !ShouldInsertChar(ch, flags)) { |
+ return; |
+ } |
+ |
+ OnBeforeUserAction(); |
+ skip_input_method_cancel_composition_ = true; |
+ if (insert_) |
+ model_->InsertChar(ch); |
+ else |
+ model_->ReplaceChar(ch); |
+ skip_input_method_cancel_composition_ = false; |
+ UpdateAfterChange(true, true); |
+ OnAfterUserAction(); |
+} |
+ |
+ui::TextInputType NativeTextfieldViews::GetTextInputType() { |
+ if (textfield_->read_only() || !textfield_->IsEnabled()) |
+ return ui::TEXT_INPUT_TYPE_NONE; |
+ else if (textfield_->IsPassword()) |
+ return ui::TEXT_INPUT_TYPE_PASSWORD; |
+ return ui::TEXT_INPUT_TYPE_TEXT; |
+} |
+ |
+gfx::Rect NativeTextfieldViews::GetCaretBounds() { |
+ return cursor_bounds_; |
+} |
+ |
+bool NativeTextfieldViews::HasCompositionText() { |
+ return model_->HasCompositionText(); |
+} |
+ |
+bool NativeTextfieldViews::GetTextRange(ui::Range* range) { |
+ // We don't allow the input method to retrieve or delete content from a |
+ // password box. |
+ if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT) |
+ return false; |
+ |
+ model_->GetTextRange(range); |
+ return true; |
+} |
+ |
+bool NativeTextfieldViews::GetCompositionTextRange(ui::Range* range) { |
+ if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT) |
+ return false; |
+ |
+ model_->GetCompositionTextRange(range); |
+ return true; |
+} |
+ |
+bool NativeTextfieldViews::GetSelectionRange(ui::Range* range) { |
+ if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT) |
+ return false; |
+ |
+ model_->GetSelectedRange(range); |
+ return true; |
+} |
+ |
+bool NativeTextfieldViews::SetSelectionRange(const ui::Range& range) { |
+ if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT || !range.IsValid()) |
+ return false; |
+ |
+ OnBeforeUserAction(); |
+ SelectRange(range); |
+ OnAfterUserAction(); |
+ return true; |
+} |
+ |
+bool NativeTextfieldViews::DeleteRange(const ui::Range& range) { |
+ if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT || range.is_empty()) |
+ return false; |
+ |
+ OnBeforeUserAction(); |
+ model_->SelectRange(range); |
+ if (model_->HasSelection()) { |
+ model_->DeleteSelection(); |
+ UpdateAfterChange(true, true); |
+ } |
+ OnAfterUserAction(); |
+ return true; |
+} |
+ |
+bool NativeTextfieldViews::GetTextFromRange( |
+ const ui::Range& range, |
+ const base::Callback<void(const string16&)>& callback) { |
+ if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT || range.is_empty()) |
+ return false; |
+ |
+ callback.Run(model_->GetTextFromRange(range)); |
+ return true; |
+} |
+ |
+void NativeTextfieldViews::OnInputMethodChanged() { |
+ NOTIMPLEMENTED(); |
+} |
+ |
+bool NativeTextfieldViews::ChangeTextDirectionAndLayoutAlignment( |
+ base::i18n::TextDirection direction) { |
+ NOTIMPLEMENTED(); |
+ return false; |
+} |
+ |
+View* NativeTextfieldViews::GetOwnerViewOfTextInputClient() { |
+ return textfield_; |
+} |
+ |
+void NativeTextfieldViews::OnCompositionTextConfirmedOrCleared() { |
+ if (skip_input_method_cancel_composition_) |
+ return; |
+ DCHECK(textfield_->GetInputMethod()); |
+ textfield_->GetInputMethod()->CancelComposition(textfield_); |
+} |
const gfx::Font& NativeTextfieldViews::GetFont() const { |
return textfield_->font(); |
@@ -475,6 +643,8 @@ void NativeTextfieldViews::UpdateCursorBoundsAndTextOffset() { |
} |
// shift cursor bounds to fit insets. |
cursor_bounds_.set_x(cursor_bounds_.x() + text_offset_ + insets.left()); |
+ |
+ OnCaretBoundsChanged(); |
} |
void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) { |
@@ -501,17 +671,22 @@ void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) { |
fragments.begin(); |
iter != fragments.end(); |
iter++) { |
- string16 text = model_->GetVisibleText((*iter).begin, (*iter).end); |
+ string16 text = model_->GetVisibleText(iter->start, iter->end); |
+ |
+ gfx::Font font = GetFont(); |
+ if (iter->underline) |
+ font = font.DeriveFont(0, font.GetStyle() | gfx::Font::UNDERLINED); |
+ |
// TODO(oshima): This does not give the accurate position due to |
// kerning. Figure out how webkit does this with skia. |
- int width = GetFont().GetStringWidth(text); |
+ int width = font.GetStringWidth(text); |
- if ((*iter).selected) { |
+ if (iter->selected) { |
canvas->FillRectInt(selection_color, x_offset, y, width, text_height); |
- canvas->DrawStringInt(text, GetFont(), kSelectedTextColor, |
+ canvas->DrawStringInt(text, font, kSelectedTextColor, |
x_offset, y, width, text_height); |
} else { |
- canvas->DrawStringInt(text, GetFont(), text_color, |
+ canvas->DrawStringInt(text, font, text_color, |
x_offset, y, width, text_height); |
} |
x_offset += width; |
@@ -577,7 +752,7 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { |
cursor_changed = true; |
break; |
case ui::VKEY_HOME: |
- model_->MoveCursorToStart(selection); |
+ model_->MoveCursorToHome(selection); |
cursor_changed = true; |
break; |
case ui::VKEY_BACK: |
@@ -590,7 +765,7 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { |
#if defined(OS_WIN) |
break; |
#else |
- model_->MoveCursorToStart(true); |
+ model_->MoveCursorToHome(true); |
#endif |
} else if (control) { |
// If only control is pressed, then erase the previous word. |
@@ -626,14 +801,9 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { |
default: |
break; |
} |
- char16 ch = key_event.GetCharacter(); |
- if (editable && ShouldInsertChar(ch, key_event.flags())) { |
- if (insert_) |
- model_->Insert(ch); |
- else |
- model_->Replace(ch); |
- text_changed = true; |
- } |
+ |
+ // We must have input method in order to support text input. |
+ DCHECK(textfield_->GetInputMethod()); |
UpdateAfterChange(text_changed, cursor_changed); |
OnAfterUserAction(); |
@@ -717,9 +887,6 @@ void NativeTextfieldViews::SetCursorForMouseClick(const views::MouseEvent& e) { |
void NativeTextfieldViews::PropagateTextChange() { |
textfield_->SyncText(); |
- TextfieldController* controller = textfield_->GetController(); |
- if (controller) |
- controller->ContentsChanged(textfield_, GetText()); |
} |
void NativeTextfieldViews::UpdateAfterChange(bool text_changed, |
@@ -750,6 +917,16 @@ void NativeTextfieldViews::InitContextMenuIfRequired() { |
context_menu_menu_.reset(new Menu2(context_menu_contents_.get())); |
} |
+void NativeTextfieldViews::OnTextInputTypeChanged() { |
+ DCHECK(textfield_->GetInputMethod()); |
+ textfield_->GetInputMethod()->OnTextInputTypeChanged(textfield_); |
+} |
+ |
+void NativeTextfieldViews::OnCaretBoundsChanged() { |
+ DCHECK(textfield_->GetInputMethod()); |
+ textfield_->GetInputMethod()->OnCaretBoundsChanged(textfield_); |
+} |
+ |
void NativeTextfieldViews::OnBeforeUserAction() { |
TextfieldController* controller = textfield_->GetController(); |
if (controller) |