Chromium Code Reviews| 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 1cc0c23911acbe637f83876cb1c993e5245a3ebd..b38c2409b405e658c57eed954e444f848658b9d3 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_cancel_composition_(false), |
| ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)), |
| last_mouse_press_time_(base::Time::FromInternalValue(0)), |
| click_state_(NONE) { |
| @@ -164,14 +166,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) { |
| @@ -180,10 +177,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 { |
| @@ -224,6 +217,7 @@ void NativeTextfieldViews::UpdateBackgroundColor() { |
| void NativeTextfieldViews::UpdateReadOnly() { |
| SchedulePaint(); |
| + OnTextInputTypeChanged(); |
| } |
| void NativeTextfieldViews::UpdateFont() { |
| @@ -234,11 +228,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() { |
| @@ -279,7 +275,7 @@ gfx::NativeView NativeTextfieldViews::GetTestingHandle() const { |
| } |
| bool NativeTextfieldViews::IsIMEComposing() const { |
| - return false; |
| + return model_->HasComposition(); |
| } |
| void NativeTextfieldViews::GetSelectedRange(ui::Range* range) const { |
| @@ -311,6 +307,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, |
| @@ -327,6 +324,10 @@ void NativeTextfieldViews::HandleBlur() { |
| } |
| } |
| +TextInputClient* NativeTextfieldViews::GetTextInputClient() { |
| + return textfield_->read_only() ? NULL : this; |
| +} |
| + |
| ///////////////////////////////////////////////////////////////// |
| // NativeTextfieldViews, ui::SimpleMenuModel::Delegate overrides: |
| @@ -418,6 +419,164 @@ void NativeTextfieldViews::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
| /////////////////////////////////////////////////////////////////////////////// |
| // NativeTextfieldViews private: |
|
oshima
2011/03/22 01:50:39
NativeTextfieldViews, TextInputClient implementati
James Su
2011/03/22 08:41:14
Done.
|
| +void NativeTextfieldViews::SetComposition(const ui::Composition& composition) { |
| + if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) |
| + return; |
| + |
| + OnBeforeUserAction(); |
| + skip_cancel_composition_ = true; |
| + model_->SetComposition(composition); |
| + skip_cancel_composition_ = false; |
| + UpdateAfterChange(true, true); |
| + OnAfterUserAction(); |
| +} |
| + |
| +void NativeTextfieldViews::ConfirmComposition() { |
| + if (!model_->HasComposition()) |
| + return; |
| + |
| + OnBeforeUserAction(); |
| + skip_cancel_composition_ = true; |
| + model_->ConfirmComposition(); |
| + skip_cancel_composition_ = false; |
| + UpdateAfterChange(true, true); |
| + OnAfterUserAction(); |
| +} |
| + |
| +void NativeTextfieldViews::ClearComposition() { |
| + if (!model_->HasComposition()) |
| + return; |
| + |
| + OnBeforeUserAction(); |
| + skip_cancel_composition_ = true; |
| + model_->ClearComposition(); |
| + skip_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_cancel_composition_ = true; |
| + if (insert_) |
| + model_->InsertText(text); |
| + else |
| + model_->ReplaceText(text); |
| + skip_cancel_composition_ = false; |
| + UpdateAfterChange(true, true); |
| + OnAfterUserAction(); |
| +} |
| + |
| +void NativeTextfieldViews::InsertChar(char16 ch, int flags) { |
| + if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || !IsValidChar(ch, flags)) |
| + return; |
| + |
| + OnBeforeUserAction(); |
| + skip_cancel_composition_ = true; |
| + if (insert_) |
| + model_->InsertChar(ch); |
| + else |
| + model_->ReplaceChar(ch); |
| + skip_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::HasComposition() { |
| + return model_->HasComposition(); |
| +} |
| + |
| +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::GetCompositionRange(ui::Range* range) { |
| + if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT) |
| + return false; |
| + |
| + model_->GetCompositionRange(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(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; |
| +} |
| + |
| +void NativeTextfieldViews::OnCompositionConfirmedOrCleared() { |
| + if (!skip_cancel_composition_) |
| + CancelComposition(); |
| +} |
| + |
| const gfx::Font& NativeTextfieldViews::GetFont() const { |
| return textfield_->font(); |
| } |
| @@ -476,6 +635,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) { |
| @@ -502,17 +663,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; |
| @@ -578,7 +744,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: |
| @@ -591,7 +757,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. |
| @@ -627,13 +793,17 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { |
| default: |
| break; |
| } |
| - char16 ch = key_event.GetCharacter(); |
| - if (!control && editable && IsValidChar(ch, key_event.flags())) { |
| - if (insert_) |
| - model_->Insert(ch); |
| - else |
| - model_->Replace(ch); |
| - text_changed = true; |
| + |
| + // Only handle text input by ourselves if there is no input method. |
| + if (!textfield_->GetInputMethod()) { |
| + char16 ch = key_event.GetCharacter(); |
| + if (!control && editable && IsValidChar(ch, key_event.flags())) { |
| + if (insert_) |
| + model_->InsertChar(ch); |
| + else |
| + model_->ReplaceChar(ch); |
| + text_changed = true; |
| + } |
| } |
| UpdateAfterChange(text_changed, cursor_changed); |
| @@ -718,9 +888,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, |
| @@ -751,6 +918,33 @@ void NativeTextfieldViews::InitContextMenuIfRequired() { |
| context_menu_menu_.reset(new Menu2(context_menu_contents_.get())); |
| } |
| +void NativeTextfieldViews::OnTextInputTypeChanged() { |
| + if (!textfield_->HasFocus()) |
| + return; |
| + |
| + InputMethod* input_method = textfield_->GetInputMethod(); |
| + if (input_method) |
| + input_method->OnTextInputTypeChanged(textfield_); |
| +} |
| + |
| +void NativeTextfieldViews::OnCaretBoundsChanged() { |
| + if (!textfield_->HasFocus()) |
| + return; |
| + |
| + InputMethod* input_method = textfield_->GetInputMethod(); |
| + if (input_method) |
| + input_method->OnCaretBoundsChanged(textfield_); |
| +} |
| + |
| +void NativeTextfieldViews::CancelComposition() { |
| + if (!textfield_->HasFocus()) |
| + return; |
| + |
| + InputMethod* input_method = textfield_->GetInputMethod(); |
| + if (input_method) |
| + input_method->CancelComposition(textfield_); |
| +} |
| + |
| void NativeTextfieldViews::OnBeforeUserAction() { |
| TextfieldController* controller = textfield_->GetController(); |
| if (controller) |