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 2a6a4c6666ec62b437c611f807c80d183bf1a388..91dd47d615f97e71ca02d218b01aac4e0db079fe 100644 |
| --- a/views/controls/textfield/native_textfield_views.cc |
| +++ b/views/controls/textfield/native_textfield_views.cc |
| @@ -16,12 +16,12 @@ |
| #include "ui/base/range/range.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/insets.h" |
| +#include "ui/gfx/render_text.h" |
| #include "views/background.h" |
| #include "views/border.h" |
| #include "views/controls/focusable_border.h" |
| #include "views/controls/menu/menu_item_view.h" |
| #include "views/controls/menu/menu_model_adapter.h" |
| -#include "views/controls/textfield/text_style.h" |
| #include "views/controls/textfield/textfield.h" |
| #include "views/controls/textfield/textfield_controller.h" |
| #include "views/controls/textfield/textfield_views_model.h" |
| @@ -37,16 +37,6 @@ |
| namespace { |
| -// Color settings for text, backgrounds and cursor. |
| -// These are tentative, and should be derived from theme, system |
| -// settings and current settings. |
| -// TODO(oshima): Change this to match the standard chrome |
| -// before dogfooding textfield views. |
| -const SkColor kSelectedTextColor = SK_ColorWHITE; |
| -const SkColor kFocusedSelectionColor = SK_ColorCYAN; |
| -const SkColor kUnfocusedSelectionColor = SK_ColorLTGRAY; |
| -const SkColor kCursorColor = SK_ColorBLACK; |
| - |
| // Parameters to control cursor blinking. |
| const int kCursorVisibleTimeMs = 800; |
| const int kCursorInvisibleTimeMs = 500; |
| @@ -62,8 +52,6 @@ NativeTextfieldViews::NativeTextfieldViews(Textfield* parent) |
| : textfield_(parent), |
| ALLOW_THIS_IN_INITIALIZER_LIST(model_(new TextfieldViewsModel(this))), |
| text_border_(new FocusableBorder()), |
| - text_offset_(0), |
| - insert_(true), |
| is_cursor_visible_(false), |
| skip_input_method_cancel_composition_(false), |
| initiating_drag_(false), |
| @@ -76,6 +64,12 @@ NativeTextfieldViews::NativeTextfieldViews(Textfield* parent) |
| // Lowercase is not supported. |
| DCHECK_NE(parent->style(), Textfield::STYLE_LOWERCASE); |
| + // Set the default text style. |
| + gfx::StyleRange default_style; |
| + default_style.font = textfield_->font(); |
| + default_style.foreground = textfield_->text_color(); |
| + GetRenderText()->set_default_style(default_style); |
| + |
| set_context_menu_controller(this); |
| set_drag_controller(this); |
| } |
| @@ -179,7 +173,8 @@ int NativeTextfieldViews::OnDragUpdated(const DropTargetEvent& event) { |
| bool is_point_in_selection = IsPointInSelection(event.location()); |
| is_drop_cursor_visible_ = !is_point_in_selection; |
| // TODO(msw): Pan over text when the user drags to the visible text edge. |
| - UpdateCursorBoundsAndTextOffset(FindCursorPosition(event.location()), true); |
| + size_t cursor_pos = GetRenderText()->FindCursorPosition(event.location()); |
| + OnCaretBoundsChanged(); |
| SchedulePaint(); |
| if (initiating_drag_) { |
| @@ -197,7 +192,8 @@ int NativeTextfieldViews::OnPerformDrop(const DropTargetEvent& event) { |
| OnBeforeUserAction(); |
| skip_input_method_cancel_composition_ = true; |
| - size_t drop_destination = FindCursorPosition(event.location()); |
| + size_t drop_destination = |
| + GetRenderText()->FindCursorPosition(event.location()); |
| string16 text; |
| event.data().GetString(&text); |
| @@ -215,7 +211,7 @@ int NativeTextfieldViews::OnPerformDrop(const DropTargetEvent& event) { |
| model_->DeleteSelectionAndInsertTextAt(text, drop_destination); |
| } else { |
| model_->MoveCursorTo(drop_destination, false); |
| - // Drop always inserts a text even if insert_ == false. |
| + // Drop always inserts text even if the textfield is not in insert mode. |
| model_->InsertText(text); |
| } |
| skip_input_method_cancel_composition_ = false; |
| @@ -299,12 +295,12 @@ bool NativeTextfieldViews::CanStartDragForView(View* sender, |
| // NativeTextfieldViews, NativeTextifieldWrapper overrides: |
| string16 NativeTextfieldViews::GetText() const { |
| - return model_->text(); |
| + return model_->GetText(); |
| } |
| void NativeTextfieldViews::UpdateText() { |
| model_->SetText(textfield_->text()); |
| - UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); |
| + OnCaretBoundsChanged(); |
| SchedulePaint(); |
| } |
| @@ -312,7 +308,7 @@ void NativeTextfieldViews::AppendText(const string16& text) { |
| if (text.empty()) |
| return; |
| model_->Append(text); |
| - UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); |
| + OnCaretBoundsChanged(); |
| SchedulePaint(); |
| } |
| @@ -358,12 +354,12 @@ void NativeTextfieldViews::UpdateReadOnly() { |
| } |
| void NativeTextfieldViews::UpdateFont() { |
| - UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); |
| + OnCaretBoundsChanged(); |
| } |
| void NativeTextfieldViews::UpdateIsPassword() { |
| model_->set_is_password(textfield_->IsPassword()); |
| - UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); |
| + OnCaretBoundsChanged(); |
| SchedulePaint(); |
| OnTextInputTypeChanged(); |
| } |
| @@ -385,7 +381,7 @@ void NativeTextfieldViews::UpdateHorizontalMargins() { |
| gfx::Insets inset = GetInsets(); |
| text_border_->SetInsets(inset.top(), left, inset.bottom(), right); |
| - UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); |
| + OnCaretBoundsChanged(); |
| } |
| void NativeTextfieldViews::UpdateVerticalMargins() { |
| @@ -393,9 +389,8 @@ void NativeTextfieldViews::UpdateVerticalMargins() { |
| if (!textfield_->GetVerticalMargins(&top, &bottom)) |
| return; |
| gfx::Insets inset = GetInsets(); |
| - |
| text_border_->SetInsets(top, inset.left(), bottom, inset.right()); |
| - UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); |
| + OnCaretBoundsChanged(); |
| } |
| bool NativeTextfieldViews::SetFocus() { |
| @@ -421,12 +416,12 @@ void NativeTextfieldViews::GetSelectedRange(ui::Range* range) const { |
| void NativeTextfieldViews::SelectRange(const ui::Range& range) { |
| model_->SelectRange(range); |
| - UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); |
| + OnCaretBoundsChanged(); |
| SchedulePaint(); |
| } |
| size_t NativeTextfieldViews::GetCursorPosition() const { |
| - return model_->cursor_pos(); |
| + return model_->GetCursorPosition(); |
| } |
| bool NativeTextfieldViews::HandleKeyPressed(const KeyEvent& e) { |
| @@ -442,6 +437,7 @@ bool NativeTextfieldViews::HandleKeyReleased(const KeyEvent& e) { |
| } |
| void NativeTextfieldViews::HandleFocus() { |
| + GetRenderText()->set_focused(true); |
| is_cursor_visible_ = true; |
| SchedulePaint(); |
| OnCaretBoundsChanged(); |
| @@ -453,6 +449,7 @@ void NativeTextfieldViews::HandleFocus() { |
| } |
| void NativeTextfieldViews::HandleBlur() { |
| + GetRenderText()->set_focused(false); |
| // Stop blinking cursor. |
| cursor_timer_.RevokeAll(); |
| if (is_cursor_visible_) { |
| @@ -536,23 +533,25 @@ void NativeTextfieldViews::ExecuteCommand(int command_id) { |
| OnAfterUserAction(); |
| } |
| -TextStyle* NativeTextfieldViews::CreateTextStyle() { |
| - return model_->CreateTextStyle(); |
| -} |
| - |
| -void NativeTextfieldViews::ApplyTextStyle(const TextStyle* style, |
| - const ui::Range& range) { |
| - model_->ApplyTextStyle(style, range); |
| +void NativeTextfieldViews::ApplyStyleRange(const gfx::StyleRange& style) { |
| + GetRenderText()->ApplyStyleRange(style); |
| SchedulePaint(); |
| } |
| -void NativeTextfieldViews::ClearAllTextStyles() { |
| - model_->ClearAllTextStyles(); |
| +void NativeTextfieldViews::ApplyDefaultStyle() { |
| + GetRenderText()->ApplyDefaultStyle(); |
| SchedulePaint(); |
| } |
| void NativeTextfieldViews::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
| - UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); |
| + // Set the RenderText display area. |
| + gfx::Insets insets = GetInsets(); |
| + gfx::Rect display_rect(insets.left(), |
| + insets.top(), |
| + width() - insets.width(), |
| + height() - insets.height()); |
| + GetRenderText()->set_display_rect(display_rect); |
| + OnCaretBoundsChanged(); |
| } |
| /////////////////////////////////////////////////////////////////////////////// |
| @@ -602,7 +601,7 @@ void NativeTextfieldViews::InsertText(const string16& text) { |
| OnBeforeUserAction(); |
| skip_input_method_cancel_composition_ = true; |
| - if (insert_) |
| + if (GetRenderText()->get_insert_mode()) |
| model_->InsertText(text); |
| else |
| model_->ReplaceText(text); |
| @@ -619,7 +618,7 @@ void NativeTextfieldViews::InsertChar(char16 ch, int flags) { |
| OnBeforeUserAction(); |
| skip_input_method_cancel_composition_ = true; |
| - if (insert_) |
| + if (GetRenderText()->get_insert_mode()) |
| model_->InsertChar(ch); |
| else |
| model_->ReplaceChar(ch); |
| @@ -637,7 +636,9 @@ ui::TextInputType NativeTextfieldViews::GetTextInputType() { |
| } |
| gfx::Rect NativeTextfieldViews::GetCaretBounds() { |
| - return cursor_bounds_; |
| + gfx::RenderText* render_text = GetRenderText(); |
| + return render_text->GetCursorBounds(render_text->GetCursor(), |
| + render_text->get_insert_mode()); |
| } |
| bool NativeTextfieldViews::HasCompositionText() { |
| @@ -725,12 +726,8 @@ void NativeTextfieldViews::OnCompositionTextConfirmedOrCleared() { |
| textfield_->GetInputMethod()->CancelComposition(textfield_); |
| } |
| -const gfx::Font& NativeTextfieldViews::GetFont() const { |
| - return textfield_->font(); |
| -} |
| - |
| -SkColor NativeTextfieldViews::GetTextColor() const { |
| - return textfield_->text_color(); |
| +gfx::RenderText* NativeTextfieldViews::GetRenderText() const { |
| + return model_->get_render_text(); |
| } |
| void NativeTextfieldViews::UpdateCursor() { |
| @@ -743,108 +740,18 @@ void NativeTextfieldViews::UpdateCursor() { |
| } |
| void NativeTextfieldViews::RepaintCursor() { |
| - gfx::Rect r = cursor_bounds_; |
| + gfx::Rect r(GetCaretBounds()); |
| r.Inset(-1, -1, -1, -1); |
| SchedulePaintInRect(r); |
| } |
| -gfx::Rect NativeTextfieldViews::GetCursorBounds(size_t cursor_pos, |
| - bool insert_mode) const { |
| - string16 text = model_->GetVisibleText(); |
| - const gfx::Font& font = GetFont(); |
| - int x = font.GetStringWidth(text.substr(0U, cursor_pos)); |
| - DCHECK_GE(x, 0); |
| - int h = std::min(height() - GetInsets().height(), font.GetHeight()); |
| - gfx::Rect bounds(x, (height() - h) / 2, 0, h); |
| - if (!insert_mode && text.length() != cursor_pos) |
| - bounds.set_width(font.GetStringWidth(text.substr(0, cursor_pos + 1)) - x); |
| - return bounds; |
| -} |
| - |
| - |
| -void NativeTextfieldViews::UpdateCursorBoundsAndTextOffset(size_t cursor_pos, |
| - bool insert_mode) { |
| - if (bounds().IsEmpty()) |
| - return; |
| - |
| - // TODO(oshima): bidi |
| - int width = bounds().width() - GetInsets().width(); |
| - int full_width = GetFont().GetStringWidth(model_->GetVisibleText()); |
| - cursor_bounds_ = GetCursorBounds(cursor_pos, insert_mode); |
| - |
| - if (full_width < width) { |
| - // Show all text whenever the text fits to the size. |
| - text_offset_ = 0; |
| - } else if ((text_offset_ + cursor_bounds_.right()) > width) { |
| - // when the cursor overflows to the right |
| - text_offset_ = width - cursor_bounds_.right(); |
| - } else if ((text_offset_ + cursor_bounds_.x()) < 0) { |
| - // when the cursor overflows to the left |
| - text_offset_ = -cursor_bounds_.x(); |
| - } else if (full_width > width && text_offset_ + full_width < width) { |
| - // when the cursor moves within the textfield with the text |
| - // longer than the field. |
| - text_offset_ = width - full_width; |
| - } else { |
| - // move cursor freely. |
| - } |
| - // shift cursor bounds to fit insets. |
| - cursor_bounds_.set_x(cursor_bounds_.x() + text_offset_ + GetInsets().left()); |
| - |
| - OnCaretBoundsChanged(); |
| -} |
| - |
| void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) { |
| - gfx::Insets insets = GetInsets(); |
| - |
| canvas->Save(); |
| - canvas->ClipRectInt(insets.left(), insets.top(), |
| - width() - insets.width(), height() - insets.height()); |
| - |
| - // TODO(oshima): bidi support |
| - // TODO(varunjain): re-implement this so only that dirty text is painted. |
| - TextfieldViewsModel::TextFragments fragments; |
| - model_->GetFragments(&fragments); |
| - int x_offset = text_offset_ + insets.left(); |
| - int y = insets.top(); |
| - int text_height = height() - insets.height(); |
| - SkColor selection_color = |
| - textfield_->HasFocus() ? |
| - kFocusedSelectionColor : kUnfocusedSelectionColor; |
| - gfx::Font font = GetFont(); |
| - gfx::Rect selection_bounds = model_->GetSelectionBounds(font); |
| - |
| - if (!selection_bounds.IsEmpty()) { |
| - canvas->FillRectInt(selection_color, |
| - x_offset + selection_bounds.x(), |
| - (height() - selection_bounds.height()) / 2, |
| - selection_bounds.width(), |
| - selection_bounds.height()); |
| - } |
| - |
| - for (TextfieldViewsModel::TextFragments::const_iterator iter = |
| - fragments.begin(); |
| - iter != fragments.end(); |
| - iter++) { |
| - string16 text = model_->GetVisibleText(iter->range.start(), |
| - iter->range.end()); |
| - // TODO(oshima): This does not give the accurate position due to |
| - // kerning. Figure out how to do. |
| - int width = font.GetStringWidth(text); |
| - iter->style->DrawString(canvas, text, font, textfield_->read_only(), |
| - x_offset, y, width, text_height); |
| - x_offset += width; |
| - } |
| + GetRenderText()->set_cursor_visible(is_drop_cursor_visible_ || |
| + (is_cursor_visible_ && !model_->HasSelection())); |
| + // Draw the text, cursor, and selection. |
| + GetRenderText()->Draw(canvas); |
| canvas->Restore(); |
| - |
| - // Paint cursor. Replace cursor is drawn as rectangle for now. |
| - if (textfield_->IsEnabled() && (is_drop_cursor_visible_ || |
| - (is_cursor_visible_ && !model_->HasSelection()))) |
| - canvas->DrawRectInt(kCursorColor, |
| - cursor_bounds_.x(), |
| - cursor_bounds_.y(), |
| - cursor_bounds_.width(), |
| - cursor_bounds_.height()); |
| } |
| bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { |
| @@ -889,21 +796,21 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { |
| cursor_changed = text_changed = Paste(); |
| break; |
| case ui::VKEY_RIGHT: |
| - control ? model_->MoveCursorToNextWord(selection) |
| + control ? model_->MoveCursorRightByWord(selection) |
| : model_->MoveCursorRight(selection); |
| cursor_changed = true; |
| break; |
| case ui::VKEY_LEFT: |
| - control ? model_->MoveCursorToPreviousWord(selection) |
| + control ? model_->MoveCursorLeftByWord(selection) |
| : model_->MoveCursorLeft(selection); |
| cursor_changed = true; |
| break; |
| case ui::VKEY_END: |
| - model_->MoveCursorToEnd(selection); |
| + model_->MoveCursorToRightEnd(selection); |
| cursor_changed = true; |
| break; |
| case ui::VKEY_HOME: |
| - model_->MoveCursorToHome(selection); |
| + model_->MoveCursorToLeftEnd(selection); |
| cursor_changed = true; |
| break; |
| case ui::VKEY_BACK: |
| @@ -916,11 +823,11 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { |
| #if defined(OS_WIN) |
| break; |
| #else |
| - model_->MoveCursorToHome(true); |
| + model_->MoveCursorToLeftEnd(true); |
| #endif |
| } else if (control) { |
| // If only control is pressed, then erase the previous word. |
| - model_->MoveCursorToPreviousWord(true); |
| + model_->MoveCursorLeftByWord(true); |
| } |
| } |
| text_changed = model_->Backspace(); |
| @@ -936,17 +843,17 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { |
| #if defined(OS_WIN) |
| break; |
| #else |
| - model_->MoveCursorToEnd(true); |
| + model_->MoveCursorToRightEnd(true); |
| #endif |
| } else if (control) { |
| // If only control is pressed, then erase the next word. |
| - model_->MoveCursorToNextWord(true); |
| + model_->MoveCursorRightByWord(true); |
| } |
| } |
| cursor_changed = text_changed = model_->Delete(); |
| break; |
| case ui::VKEY_INSERT: |
| - insert_ = !insert_; |
| + GetRenderText()->set_insert_mode(!GetRenderText()->get_insert_mode()); |
|
oshima
2011/07/15 21:21:36
may be flip_insert_mode() is simpler.
msw
2011/07/19 07:11:22
Done.
|
| cursor_changed = true; |
| break; |
| default: |
| @@ -963,49 +870,17 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { |
| return false; |
| } |
| -size_t NativeTextfieldViews::FindCursorPosition(const gfx::Point& point) const { |
| - // TODO(oshima): BIDI/i18n support. |
| - gfx::Font font = GetFont(); |
| - gfx::Insets insets = GetInsets(); |
| - string16 text = model_->GetVisibleText(); |
| - int left = 0; |
| - int left_pos = 0; |
| - int right = font.GetStringWidth(text); |
| - int right_pos = text.length(); |
| - |
| - int x = point.x() - insets.left() - text_offset_; |
| - if (x <= left) return left_pos; |
| - if (x >= right) return right_pos; |
| - // binary searching the cursor position. |
| - // TODO(oshima): use the center of character instead of edge. |
| - // Binary search may not work for language like arabic. |
| - while (std::abs(static_cast<long>(right_pos - left_pos) > 1)) { |
| - int pivot_pos = left_pos + (right_pos - left_pos) / 2; |
| - int pivot = font.GetStringWidth(text.substr(0, pivot_pos)); |
| - if (pivot < x) { |
| - left = pivot; |
| - left_pos = pivot_pos; |
| - } else if (pivot == x) { |
| - return pivot_pos; |
| - } else { |
| - right = pivot; |
| - right_pos = pivot_pos; |
| - } |
| - } |
| - return left_pos; |
| -} |
| - |
| bool NativeTextfieldViews::IsPointInSelection(const gfx::Point& point) const { |
| ui::Range range; |
| GetSelectedRange(&range); |
| - size_t pos = FindCursorPosition(point); |
| + size_t pos = GetRenderText()->FindCursorPosition(point); |
| return (pos >= range.GetMin() && pos < range.GetMax()); |
| } |
| bool NativeTextfieldViews::MoveCursorTo(const gfx::Point& point, bool select) { |
| - size_t pos = FindCursorPosition(point); |
| + size_t pos = GetRenderText()->FindCursorPosition(point); |
| if (model_->MoveCursorTo(pos, select)) { |
| - UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); |
| + OnCaretBoundsChanged(); |
| return true; |
| } |
| return false; |
| @@ -1024,7 +899,7 @@ void NativeTextfieldViews::UpdateAfterChange(bool text_changed, |
| RepaintCursor(); |
| } |
| if (text_changed || cursor_changed) { |
| - UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); |
| + OnCaretBoundsChanged(); |
| SchedulePaint(); |
| } |
| } |
| @@ -1078,7 +953,7 @@ bool NativeTextfieldViews::Paste() { |
| // Calls TextfieldController::ContentsChanged() explicitly if the paste action |
| // did not change the content at all. See http://crbug.com/79002 |
| - if (success && model_->text() == textfield_->text()) { |
| + if (success && GetText() == textfield_->text()) { |
| TextfieldController* controller = textfield_->GetController(); |
| if (controller) |
| controller->ContentsChanged(textfield_, textfield_->text()); |