diff options
author | msw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-25 05:11:57 +0000 |
---|---|---|
committer | msw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-25 05:11:57 +0000 |
commit | 689d9d0d463d2b2f622a0cd74d79d1b60f432fc1 (patch) | |
tree | 9641f9d6b5fffa1d8a477a0dbfd6caeddd0772d6 /views/controls | |
parent | 906625821f97a508d76810fba7b9641335f68b51 (diff) | |
download | chromium_src-689d9d0d463d2b2f622a0cd74d79d1b60f432fc1.zip chromium_src-689d9d0d463d2b2f622a0cd74d79d1b60f432fc1.tar.gz chromium_src-689d9d0d463d2b2f622a0cd74d79d1b60f432fc1.tar.bz2 |
Add gfx::RenderText and support code.
RenderText is NativeTextFieldViews' interface to platform-specific text rendering engines.
This change doesn't hook in any new Pango or Uniscribe functionality, it will just setup the necessary API.
Review URL: http://codereview.chromium.org/7265011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93840 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views/controls')
-rw-r--r-- | views/controls/textfield/native_textfield_gtk.cc | 10 | ||||
-rw-r--r-- | views/controls/textfield/native_textfield_gtk.h | 6 | ||||
-rw-r--r-- | views/controls/textfield/native_textfield_views.cc | 295 | ||||
-rw-r--r-- | views/controls/textfield/native_textfield_views.h | 32 | ||||
-rw-r--r-- | views/controls/textfield/native_textfield_views_unittest.cc | 10 | ||||
-rw-r--r-- | views/controls/textfield/native_textfield_win.cc | 10 | ||||
-rw-r--r-- | views/controls/textfield/native_textfield_win.h | 6 | ||||
-rw-r--r-- | views/controls/textfield/native_textfield_wrapper.h | 17 | ||||
-rw-r--r-- | views/controls/textfield/text_style.cc | 54 | ||||
-rw-r--r-- | views/controls/textfield/text_style.h | 65 | ||||
-rw-r--r-- | views/controls/textfield/textfield.cc | 14 | ||||
-rw-r--r-- | views/controls/textfield/textfield.h | 31 | ||||
-rw-r--r-- | views/controls/textfield/textfield_views_model.cc | 522 | ||||
-rw-r--r-- | views/controls/textfield/textfield_views_model.h | 122 | ||||
-rw-r--r-- | views/controls/textfield/textfield_views_model_unittest.cc | 922 |
15 files changed, 581 insertions, 1535 deletions
diff --git a/views/controls/textfield/native_textfield_gtk.cc b/views/controls/textfield/native_textfield_gtk.cc index 78fb2f9..3d799a8 100644 --- a/views/controls/textfield/native_textfield_gtk.cc +++ b/views/controls/textfield/native_textfield_gtk.cc @@ -283,17 +283,11 @@ TextInputClient* NativeTextfieldGtk::GetTextInputClient() { return NULL; } -TextStyle* NativeTextfieldGtk::CreateTextStyle() { - NOTREACHED(); - return NULL; -} - -void NativeTextfieldGtk::ApplyTextStyle(const TextStyle* style, - const ui::Range& range) { +void NativeTextfieldGtk::ApplyStyleRange(const gfx::StyleRange& style) { NOTREACHED(); } -void NativeTextfieldGtk::ClearAllTextStyles() { +void NativeTextfieldGtk::ApplyDefaultStyle() { NOTREACHED(); } diff --git a/views/controls/textfield/native_textfield_gtk.h b/views/controls/textfield/native_textfield_gtk.h index 92b4e36..8420fcb 100644 --- a/views/controls/textfield/native_textfield_gtk.h +++ b/views/controls/textfield/native_textfield_gtk.h @@ -57,10 +57,8 @@ class NativeTextfieldGtk : public NativeControlGtk, virtual void HandleFocus() OVERRIDE; virtual void HandleBlur() OVERRIDE; virtual TextInputClient* GetTextInputClient() OVERRIDE; - virtual TextStyle* CreateTextStyle() OVERRIDE; - virtual void ApplyTextStyle(const TextStyle* style, - const ui::Range& range) OVERRIDE; - virtual void ClearAllTextStyles() OVERRIDE; + virtual void ApplyStyleRange(const gfx::StyleRange& style) OVERRIDE; + virtual void ApplyDefaultStyle() OVERRIDE; virtual void ClearEditHistory() OVERRIDE; // Overridden from NativeControlGtk: diff --git a/views/controls/textfield/native_textfield_views.cc b/views/controls/textfield/native_textfield_views.cc index adc6288..b3f005d 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,15 +37,8 @@ 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; +// Text color for read only. +const SkColor kReadonlyTextColor = SK_ColorDKGRAY; // Parameters to control cursor blinking. const int kCursorVisibleTimeMs = 800; @@ -62,9 +55,8 @@ 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), + is_drop_cursor_visible_(false), skip_input_method_cancel_composition_(false), initiating_drag_(false), ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)), @@ -76,6 +68,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); } @@ -105,7 +103,7 @@ bool NativeTextfieldViews::OnMousePressed(const MouseEvent& event) { initiating_drag_ = false; switch(aggregated_clicks_) { case 0: - if (!IsPointInSelection(event.location())) + if (!GetRenderText()->IsPointInSelection(event.location())) MoveCursorTo(event.location(), event.IsShiftDown()); else initiating_drag_ = true; @@ -176,14 +174,14 @@ bool NativeTextfieldViews::CanDrop(const OSExchangeData& data) { int NativeTextfieldViews::OnDragUpdated(const DropTargetEvent& event) { DCHECK(CanDrop(event.data())); - bool is_point_in_selection = IsPointInSelection(event.location()); - is_drop_cursor_visible_ = !is_point_in_selection; + bool in_selection = GetRenderText()->IsPointInSelection(event.location()); + is_drop_cursor_visible_ = !in_selection; // TODO(msw): Pan over text when the user drags to the visible text edge. - UpdateCursorBoundsAndTextOffset(FindCursorPosition(event.location()), true); + OnCaretBoundsChanged(); SchedulePaint(); if (initiating_drag_) { - if (is_point_in_selection) + if (in_selection) return ui::DragDropTypes::DRAG_NONE; return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY : ui::DragDropTypes::DRAG_MOVE; @@ -193,11 +191,14 @@ int NativeTextfieldViews::OnDragUpdated(const DropTargetEvent& event) { int NativeTextfieldViews::OnPerformDrop(const DropTargetEvent& event) { DCHECK(CanDrop(event.data())); - DCHECK(!initiating_drag_ || !IsPointInSelection(event.location())); + DCHECK(!initiating_drag_ || + !GetRenderText()->IsPointInSelection(event.location())); OnBeforeUserAction(); skip_input_method_cancel_composition_ = true; - size_t drop_destination = FindCursorPosition(event.location()); + // TODO(msw): Remove final reference to FindCursorPosition. + size_t drop_destination = + GetRenderText()->FindCursorPosition(event.location()); string16 text; event.data().GetString(&text); @@ -215,7 +216,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; @@ -246,14 +247,15 @@ void NativeTextfieldViews::OnBlur() { } gfx::NativeCursor NativeTextfieldViews::GetCursor(const MouseEvent& event) { - bool text = !initiating_drag_ && (event.type() == ui::ET_MOUSE_DRAGGED || - !IsPointInSelection(event.location())); + 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(OS_WIN) static HCURSOR ibeam = LoadCursor(NULL, IDC_IBEAM); static HCURSOR arrow = LoadCursor(NULL, IDC_ARROW); - return text ? ibeam : arrow; + return text_cursor ? ibeam : arrow; #else - return text ? gfx::GetCursor(GDK_XTERM) : NULL; + return text_cursor ? gfx::GetCursor(GDK_XTERM) : NULL; #endif } @@ -282,7 +284,7 @@ void NativeTextfieldViews::WriteDragDataForView(views::View* sender, int NativeTextfieldViews::GetDragOperationsForView(views::View* sender, const gfx::Point& p) { - if (!textfield_->IsEnabled() || !IsPointInSelection(p)) + if (!textfield_->IsEnabled() || !GetRenderText()->IsPointInSelection(p)) return ui::DragDropTypes::DRAG_NONE; if (sender == this && !textfield_->read_only()) return ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY; @@ -292,19 +294,19 @@ int NativeTextfieldViews::GetDragOperationsForView(views::View* sender, bool NativeTextfieldViews::CanStartDragForView(View* sender, const gfx::Point& press_pt, const gfx::Point& p) { - return IsPointInSelection(press_pt); + return GetRenderText()->IsPointInSelection(press_pt); } ///////////////////////////////////////////////////////////////// // 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 +314,7 @@ void NativeTextfieldViews::AppendText(const string16& text) { if (text.empty()) return; model_->Append(text); - UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); + OnCaretBoundsChanged(); SchedulePaint(); } @@ -353,17 +355,24 @@ void NativeTextfieldViews::UpdateBackgroundColor() { } void NativeTextfieldViews::UpdateReadOnly() { + // Update the default text style. + gfx::StyleRange default_style(GetRenderText()->default_style()); + default_style.foreground = textfield_->read_only() ? kReadonlyTextColor : + textfield_->text_color(); + GetRenderText()->set_default_style(default_style); + GetRenderText()->ApplyDefaultStyle(); + SchedulePaint(); OnTextInputTypeChanged(); } 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 +394,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 +402,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 +429,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 +450,7 @@ bool NativeTextfieldViews::HandleKeyReleased(const KeyEvent& e) { } void NativeTextfieldViews::HandleFocus() { + GetRenderText()->set_focused(true); is_cursor_visible_ = true; SchedulePaint(); OnCaretBoundsChanged(); @@ -453,6 +462,7 @@ void NativeTextfieldViews::HandleFocus() { } void NativeTextfieldViews::HandleBlur() { + GetRenderText()->set_focused(false); // Stop blinking cursor. cursor_timer_.RevokeAll(); if (is_cursor_visible_) { @@ -536,23 +546,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 +614,7 @@ void NativeTextfieldViews::InsertText(const string16& text) { OnBeforeUserAction(); skip_input_method_cancel_composition_ = true; - if (insert_) + if (GetRenderText()->insert_mode()) model_->InsertText(text); else model_->ReplaceText(text); @@ -619,7 +631,7 @@ void NativeTextfieldViews::InsertChar(char16 ch, int flags) { OnBeforeUserAction(); skip_input_method_cancel_composition_ = true; - if (insert_) + if (GetRenderText()->insert_mode()) model_->InsertChar(ch); else model_->ReplaceChar(ch); @@ -637,7 +649,9 @@ ui::TextInputType NativeTextfieldViews::GetTextInputType() { } gfx::Rect NativeTextfieldViews::GetCaretBounds() { - return cursor_bounds_; + gfx::RenderText* render_text = GetRenderText(); + return render_text->GetCursorBounds(render_text->GetCursorPosition(), + render_text->insert_mode()); } bool NativeTextfieldViews::HasCompositionText() { @@ -725,12 +739,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_->render_text(); } void NativeTextfieldViews::UpdateCursor() { @@ -743,108 +753,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 +809,21 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { cursor_changed = text_changed = Paste(); break; case ui::VKEY_RIGHT: - control ? model_->MoveCursorToNextWord(selection) - : model_->MoveCursorRight(selection); + model_->MoveCursorRight( + control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK, selection); cursor_changed = true; break; case ui::VKEY_LEFT: - control ? model_->MoveCursorToPreviousWord(selection) - : model_->MoveCursorLeft(selection); + model_->MoveCursorLeft( + control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK, selection); cursor_changed = true; break; case ui::VKEY_END: - model_->MoveCursorToEnd(selection); + model_->MoveCursorRight(gfx::LINE_BREAK, selection); cursor_changed = true; break; case ui::VKEY_HOME: - model_->MoveCursorToHome(selection); + model_->MoveCursorLeft(gfx::LINE_BREAK, selection); cursor_changed = true; break; case ui::VKEY_BACK: @@ -916,11 +836,11 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { #if defined(OS_WIN) break; #else - model_->MoveCursorToHome(true); + model_->MoveCursorLeft(gfx::LINE_BREAK, true); #endif } else if (control) { // If only control is pressed, then erase the previous word. - model_->MoveCursorToPreviousWord(true); + model_->MoveCursorLeft(gfx::WORD_BREAK, true); } } text_changed = model_->Backspace(); @@ -936,17 +856,17 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { #if defined(OS_WIN) break; #else - model_->MoveCursorToEnd(true); + model_->MoveCursorRight(gfx::LINE_BREAK, true); #endif } else if (control) { // If only control is pressed, then erase the next word. - model_->MoveCursorToNextWord(true); + model_->MoveCursorRight(gfx::WORD_BREAK, true); } } cursor_changed = text_changed = model_->Delete(); break; case ui::VKEY_INSERT: - insert_ = !insert_; + GetRenderText()->toggle_insert_mode(); cursor_changed = true; break; default: @@ -963,52 +883,11 @@ 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); - return (pos >= range.GetMin() && pos < range.GetMax()); -} - bool NativeTextfieldViews::MoveCursorTo(const gfx::Point& point, bool select) { - size_t pos = FindCursorPosition(point); - if (model_->MoveCursorTo(pos, select)) { - UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); - return true; - } - return false; + if (!model_->MoveCursorTo(point, select)) + return false; + OnCaretBoundsChanged(); + return true; } void NativeTextfieldViews::PropagateTextChange() { @@ -1024,7 +903,7 @@ void NativeTextfieldViews::UpdateAfterChange(bool text_changed, RepaintCursor(); } if (text_changed || cursor_changed) { - UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); + OnCaretBoundsChanged(); SchedulePaint(); } } @@ -1078,7 +957,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()); diff --git a/views/controls/textfield/native_textfield_views.h b/views/controls/textfield/native_textfield_views.h index 4851549..ea6fec8 100644 --- a/views/controls/textfield/native_textfield_views.h +++ b/views/controls/textfield/native_textfield_views.h @@ -114,10 +114,8 @@ class NativeTextfieldViews : public View, virtual void HandleFocus() OVERRIDE; virtual void HandleBlur() OVERRIDE; virtual TextInputClient* GetTextInputClient() OVERRIDE; - virtual TextStyle* CreateTextStyle() OVERRIDE; - virtual void ApplyTextStyle(const TextStyle* style, - const ui::Range& range) OVERRIDE; - virtual void ClearAllTextStyles() OVERRIDE; + virtual void ApplyStyleRange(const gfx::StyleRange& style) OVERRIDE; + virtual void ApplyDefaultStyle() OVERRIDE; virtual void ClearEditHistory() OVERRIDE; // ui::SimpleMenuModel::Delegate overrides @@ -164,11 +162,8 @@ class NativeTextfieldViews : public View, // Overridden from TextfieldViewsModel::Delegate: virtual void OnCompositionTextConfirmedOrCleared() OVERRIDE; - // Returns the Textfield's font. - const gfx::Font& GetFont() const; - - // Returns the Textfield's text color. - SkColor GetTextColor() const; + // Returns the TextfieldViewsModel's text/cursor/selection rendering model. + gfx::RenderText* GetRenderText() const; // A callback function to periodically update the cursor state. void UpdateCursor(); @@ -176,9 +171,6 @@ class NativeTextfieldViews : public View, // Repaint the cursor. void RepaintCursor(); - // Returns the bounds of character at the current cursor. - gfx::Rect GetCursorBounds(size_t cursor_pos, bool insert_mode) const; - // Update the cursor_bounds and text_offset. void UpdateCursorBoundsAndTextOffset(size_t cursor_pos, bool insert_mode); @@ -187,12 +179,6 @@ class NativeTextfieldViews : public View, // Handle the keyevent. bool HandleKeyEvent(const KeyEvent& key_event); - // Find a cusor position for given |point| in this views coordinates. - size_t FindCursorPosition(const gfx::Point& point) const; - - // Returns true if the local point is over the selected range of text. - bool IsPointInSelection(const gfx::Point& point) const; - // Helper function to call MoveCursorTo on the TextfieldViewsModel. bool MoveCursorTo(const gfx::Point& point, bool select); @@ -236,16 +222,8 @@ class NativeTextfieldViews : public View, // The reference to the border class. The object is owned by View::border_. FocusableBorder* text_border_; - // The x offset for the text to be drawn, without insets; - int text_offset_; - - // True if the textfield is in insert mode. - bool insert_; - - // The local bounds and visibility of the textfield's text cursor. - gfx::Rect cursor_bounds_; + // The textfield's text and drop cursor visibility. bool is_cursor_visible_; - // The drop cursor is a visual cue for where dragged text will be dropped. bool is_drop_cursor_visible_; diff --git a/views/controls/textfield/native_textfield_views_unittest.cc b/views/controls/textfield/native_textfield_views_unittest.cc index 31a5e51..0c8ec2e 100644 --- a/views/controls/textfield/native_textfield_views_unittest.cc +++ b/views/controls/textfield/native_textfield_views_unittest.cc @@ -15,6 +15,7 @@ #include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/keycodes/keyboard_codes.h" +#include "ui/gfx/render_text.h" #include "views/controls/textfield/native_textfield_views.h" #include "views/controls/textfield/textfield.h" #include "views/controls/textfield/textfield_controller.h" @@ -218,9 +219,8 @@ class NativeTextfieldViewsTest : public ViewsTestBase, } int GetCursorPositionX(int cursor_pos) { - const string16 text = textfield_->text().substr(0, cursor_pos); - return textfield_view_->GetInsets().left() + textfield_view_->text_offset_ + - textfield_view_->GetFont().GetStringWidth(text); + gfx::RenderText* render_text = textfield_view_->GetRenderText(); + return render_text->GetCursorBounds(cursor_pos, false).x(); } // We need widget to populate wrapper class. @@ -254,12 +254,12 @@ TEST_F(NativeTextfieldViewsTest, ModelChangesTest) { last_contents_.clear(); textfield_->SetText(ASCIIToUTF16("this is")); - EXPECT_STR_EQ("this is", model_->text()); + EXPECT_STR_EQ("this is", model_->GetText()); EXPECT_STR_EQ("this is", textfield_->text()); EXPECT_TRUE(last_contents_.empty()); textfield_->AppendText(ASCIIToUTF16(" a test")); - EXPECT_STR_EQ("this is a test", model_->text()); + EXPECT_STR_EQ("this is a test", model_->GetText()); EXPECT_STR_EQ("this is a test", textfield_->text()); EXPECT_TRUE(last_contents_.empty()); diff --git a/views/controls/textfield/native_textfield_win.cc b/views/controls/textfield/native_textfield_win.cc index f53e67b..97e876d6 100644 --- a/views/controls/textfield/native_textfield_win.cc +++ b/views/controls/textfield/native_textfield_win.cc @@ -367,17 +367,11 @@ TextInputClient* NativeTextfieldWin::GetTextInputClient() { return NULL; } -TextStyle* NativeTextfieldWin::CreateTextStyle() { - NOTREACHED(); - return NULL; -} - -void NativeTextfieldWin::ApplyTextStyle(const TextStyle* style, - const ui::Range& range) { +void NativeTextfieldWin::ApplyStyleRange(const gfx::StyleRange& style) { NOTREACHED(); } -void NativeTextfieldWin::ClearAllTextStyles() { +void NativeTextfieldWin::ApplyDefaultStyle() { NOTREACHED(); } diff --git a/views/controls/textfield/native_textfield_win.h b/views/controls/textfield/native_textfield_win.h index bb0fc2b..ceabb34 100644 --- a/views/controls/textfield/native_textfield_win.h +++ b/views/controls/textfield/native_textfield_win.h @@ -86,10 +86,8 @@ class NativeTextfieldWin virtual void HandleFocus() OVERRIDE; virtual void HandleBlur() OVERRIDE; virtual TextInputClient* GetTextInputClient() OVERRIDE; - virtual TextStyle* CreateTextStyle() OVERRIDE; - virtual void ApplyTextStyle(const TextStyle* style, - const ui::Range& range) OVERRIDE; - virtual void ClearAllTextStyles() OVERRIDE; + virtual void ApplyStyleRange(const gfx::StyleRange& style) OVERRIDE; + virtual void ApplyDefaultStyle() OVERRIDE; virtual void ClearEditHistory() OVERRIDE; // Overridden from ui::SimpleMenuModel::Delegate: diff --git a/views/controls/textfield/native_textfield_wrapper.h b/views/controls/textfield/native_textfield_wrapper.h index 8d694eb..6cef09f 100644 --- a/views/controls/textfield/native_textfield_wrapper.h +++ b/views/controls/textfield/native_textfield_wrapper.h @@ -11,6 +11,7 @@ namespace gfx { class Insets; +struct StyleRange; } // namespace gfx namespace ui { @@ -22,7 +23,6 @@ namespace views { class KeyEvent; class Textfield; class TextInputClient; -class TextStyle; class View; // An interface implemented by an object that provides a platform-native @@ -126,17 +126,12 @@ class NativeTextfieldWrapper { // support text input. virtual TextInputClient* GetTextInputClient() = 0; - // Creates a new TextStyle for this textfield. - // See |Textfield::CreateTextStyle| for detail. - virtual TextStyle* CreateTextStyle() = 0; + // Applies the |style| to the text specified by its range. + // See |Textfield::ApplyStyleRange| for detail. + virtual void ApplyStyleRange(const gfx::StyleRange& style) = 0; - // Applies the |style| to the text specified by the |range|. - // See |Textfield::ApplyTextStyle| for detail. - virtual void ApplyTextStyle(const TextStyle* style, - const ui::Range& range) = 0; - - // Clears all text styles in this textfield. - virtual void ClearAllTextStyles() = 0; + // Applies the default style to the textfield. + virtual void ApplyDefaultStyle() = 0; // Clears Edit history. virtual void ClearEditHistory() = 0; diff --git a/views/controls/textfield/text_style.cc b/views/controls/textfield/text_style.cc deleted file mode 100644 index 18400c6..0000000 --- a/views/controls/textfield/text_style.cc +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "views/controls/textfield/text_style.h" - -#include "ui/gfx/canvas.h" -#include "ui/gfx/canvas_skia.h" -#include "ui/gfx/font.h" - -namespace { -// Text color for read only. -const SkColor kReadonlyTextColor = SK_ColorDKGRAY; - -// Strike line width. -const int kStrikeWidth = 2; -} - -namespace views { - -TextStyle::TextStyle() - : foreground_(SK_ColorBLACK), - strike_(false), - underline_(false) { -} - -TextStyle::~TextStyle() { -} - -void TextStyle::DrawString(gfx::Canvas* canvas, - string16& text, - gfx::Font& base_font, - bool readonly, - int x, int y, int width, int height) const { - SkColor text_color = readonly ? kReadonlyTextColor : foreground_; - - gfx::Font font = underline_ ? - base_font.DeriveFont(0, base_font.GetStyle() | gfx::Font::UNDERLINED) : - base_font; - canvas->DrawStringInt(text, font, text_color, x, y, width, height); - if (strike_) { - SkPaint paint; - paint.setAntiAlias(true); - paint.setStyle(SkPaint::kFill_Style); - paint.setColor(text_color); - paint.setStrokeWidth(kStrikeWidth); - canvas->AsCanvasSkia()->drawLine( - SkIntToScalar(x), SkIntToScalar(y + height), - SkIntToScalar(x + width), SkIntToScalar(y), - paint); - } -} - -} // namespace views diff --git a/views/controls/textfield/text_style.h b/views/controls/textfield/text_style.h deleted file mode 100644 index e489aee..0000000 --- a/views/controls/textfield/text_style.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef VIEWS_CONTROLS_TEXTFIELD_TEXT_STYLE_H_ -#define VIEWS_CONTROLS_TEXTFIELD_TEXT_STYLE_H_ -#pragma once - -#include "base/basictypes.h" -#include "base/gtest_prod_util.h" -#include "base/string16.h" -#include "third_party/skia/include/core/SkColor.h" - -namespace gfx { -class Canvas; -class Font; -} - -namespace views { - -// A class that specifies text style for TextfieldViews. -// TODO(suzhe|oshima): support underline color and thick style. -class TextStyle { - public: - // Foreground color for the text. - void set_foreground(SkColor color) { foreground_ = color; } - - // Draws diagnoal strike acrosss the text. - void set_strike(bool strike) { strike_ = strike; } - - // Adds underline to the text. - void set_underline(bool underline) { underline_ = underline; } - - private: - friend class NativeTextfieldViews; - friend class TextfieldViewsModel; - - FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, TextStyleTest); - FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_CompositionText); - FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, CompositionTextTest); - - TextStyle(); - virtual ~TextStyle(); - - SkColor foreground() const { return foreground_; } - bool underline() const { return underline_; } - - // Draw string to the canvas within the region given - // by |x|,|y|,|width|,|height|. - void DrawString(gfx::Canvas* canvas, - string16& text, - gfx::Font& base_font, - bool read_only, - int x, int y, int width, int height) const; - - SkColor foreground_; - bool strike_; - bool underline_; - - DISALLOW_COPY_AND_ASSIGN(TextStyle); -}; - -} // namespace views - -#endif // VIEWS_CONTROLS_TEXTFIELD_TEXT_STYLE_H_ diff --git a/views/controls/textfield/textfield.cc b/views/controls/textfield/textfield.cc index 9cd6185..a51beda 100644 --- a/views/controls/textfield/textfield.cc +++ b/views/controls/textfield/textfield.cc @@ -261,20 +261,14 @@ size_t Textfield::GetCursorPosition() const { return native_wrapper_->GetCursorPosition(); } -TextStyle* Textfield::CreateTextStyle() { +void Textfield::ApplyStyleRange(const gfx::StyleRange& style) { DCHECK(native_wrapper_); - return native_wrapper_->CreateTextStyle(); + return native_wrapper_->ApplyStyleRange(style); } -void Textfield::ApplyTextStyle(const TextStyle* style, - const ui::Range& range) { +void Textfield::ApplyDefaultStyle() { DCHECK(native_wrapper_); - return native_wrapper_->ApplyTextStyle(style, range); -} - -void Textfield::ClearAllTextStyles() { - DCHECK(native_wrapper_); - native_wrapper_->ClearAllTextStyles(); + native_wrapper_->ApplyDefaultStyle(); } void Textfield::ClearEditHistory() { diff --git a/views/controls/textfield/textfield.h b/views/controls/textfield/textfield.h index 3965050..66675d5 100644 --- a/views/controls/textfield/textfield.h +++ b/views/controls/textfield/textfield.h @@ -30,6 +30,10 @@ #include "views/controls/textfield/native_textfield_wrapper.h" #endif +namespace gfx { +struct StyleRange; +} // namespace gfx + namespace ui { class Range; } // namespace ui @@ -39,7 +43,6 @@ namespace views { class KeyEvent; class NativeTextfieldWrapper; class TextfieldController; -class TextStyle; // This class implements a View that wraps a native text (edit) field. class Textfield : public View { @@ -186,24 +189,14 @@ class Textfield : public View { // only and has to be called after the wrapper is created. size_t GetCursorPosition() const; - // Creates a new TextStyle for this textfield. The object is owned - // by the textfield and gets deleted when the textfield is deleted. - // This is views-implementation only and has to be called after the - // wrapper is created. - TextStyle* CreateTextStyle(); - - // Applies the |style| to the text specified by the |range|. If - // there is already a style applied in the |range|, the style of the - // overlapping part will be replaced by this sytle. The style will - // be ignored if range is empty or invalid. This is - // views-implementation only and has to be called after the wrapper - // is created. - void ApplyTextStyle(const TextStyle* style, const ui::Range& range); - - // Clears All TextStyles. - // This is views-implementation only and has to be called after the - // wrapper is created. - void ClearAllTextStyles(); + // Applies |style| to the text specified by its range. The style will be + // ignored if range is empty or invalid. This is views-implementation only and + // has to be called after the wrapper is created. + void ApplyStyleRange(const gfx::StyleRange& style); + + // Applies the default style to the textfield. This is views-implementation + // only and has to be called after the wrapper is created. + void ApplyDefaultStyle(); // Clears Edit history. void ClearEditHistory(); diff --git a/views/controls/textfield/textfield_views_model.cc b/views/controls/textfield/textfield_views_model.cc index 1605546..548e014 100644 --- a/views/controls/textfield/textfield_views_model.cc +++ b/views/controls/textfield/textfield_views_model.cc @@ -13,8 +13,9 @@ #include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/base/range/range.h" +#include "ui/gfx/canvas.h" #include "ui/gfx/font.h" -#include "views/controls/textfield/text_style.h" +#include "ui/gfx/render_text.h" #include "views/controls/textfield/textfield.h" #include "views/views_delegate.h" @@ -256,116 +257,8 @@ class DeleteEdit : public Edit { } }; -struct TextStyleRange { - TextStyleRange(const TextStyle* s, size_t start, size_t end) - : style(s), - range(start, end) { - } - TextStyleRange(const TextStyle* s, const ui::Range& r) - : style(s), - range(r) { - } - const TextStyle *style; - ui::Range range; -}; - } // namespace internal -namespace { - -using views::internal::TextStyleRange; - -static bool TextStyleRangeComparator(const TextStyleRange* i, - const TextStyleRange* j) { - return i->range.start() < j->range.start(); -} - -#ifndef NDEBUG -// A test function to check TextStyleRanges' invariant condition: -// "no overlapping range". -bool CheckInvariant(const TextStyleRanges* style_ranges) { - TextStyleRanges copy = *style_ranges; - std::sort(copy.begin(), copy.end(), TextStyleRangeComparator); - - for (TextStyleRanges::size_type i = 0; i < copy.size() - 1; i++) { - ui::Range& former = copy[i]->range; - ui::Range& latter = copy[i + 1]->range; - if (former.is_empty()) { - LOG(WARNING) << "Empty range at " << i << " :" << former; - return false; - } - if (!former.IsValid()) { - LOG(WARNING) << "Invalid range at " << i << " :" << former; - return false; - } - if (former.GetMax() > latter.GetMin()) { - LOG(WARNING) << - "Sorting error. former:" << former << " latter:" << latter; - return false; - } - if (former.Intersects(latter)) { - LOG(ERROR) << "overlapping style range found: former=" << former - << ", latter=" << latter; - return false; - } - } - if ((*copy.rbegin())->range.is_empty()) { - LOG(WARNING) << "Empty range at end"; - return false; - } - if (!(*copy.rbegin())->range.IsValid()) { - LOG(WARNING) << "Invalid range at end"; - return false; - } - return true; -} -#endif - -void InsertStyle(TextStyleRanges* style_ranges, - TextStyleRange* text_style_range) { - const ui::Range& range = text_style_range->range; - if (range.is_empty() || !range.IsValid()) { - delete text_style_range; - return; - } - CHECK(!range.is_reversed()); - - // Invariant condition: all items in the range has no overlaps. - TextStyleRanges::size_type index = 0; - while (index < style_ranges->size()) { - TextStyleRange* current = (*style_ranges)[index]; - if (range.Contains(current->range)) { - style_ranges->erase(style_ranges->begin() + index); - delete current; - continue; - } else if (current->range.Contains(range) && - range.start() != current->range.start() && - range.end() != current->range.end()) { - // Split current style into two styles. - style_ranges->push_back( - new TextStyleRange(current->style, - range.GetMax(), current->range.GetMax())); - current->range.set_end(range.GetMin()); - } else if (range.Intersects(current->range)) { - if (current->range.GetMax() <= range.GetMax()) { - current->range.set_end(range.GetMin()); - } else { - current->range.set_start(range.GetMax()); - } - } else { - // No change needed. Pass it through. - } - index ++; - } - // Add the new range at the end. - style_ranges->push_back(text_style_range); -#ifndef NDEBUG - DCHECK(CheckInvariant(style_ranges)); -#endif -} - -} // namespace - using internal::Edit; using internal::DeleteEdit; using internal::InsertEdit; @@ -383,70 +276,18 @@ TextfieldViewsModel::Delegate::~Delegate() { TextfieldViewsModel::TextfieldViewsModel(Delegate* delegate) : delegate_(delegate), - cursor_pos_(0), - selection_start_(0), - composition_start_(0), - composition_end_(0), + render_text_(gfx::RenderText::CreateRenderText()), is_password_(false), - current_edit_(edit_history_.end()), - sort_style_ranges_(false) { + current_edit_(edit_history_.end()) { } TextfieldViewsModel::~TextfieldViewsModel() { ClearEditHistory(); ClearComposition(); - ClearAllTextStyles(); - TextStyles::iterator begin = text_styles_.begin(); - TextStyles::iterator end = text_styles_.end(); - while (begin != end) { - TextStyles::iterator temp = begin; - ++begin; - delete *temp; - } } -void TextfieldViewsModel::GetFragments(TextFragments* fragments) { - static const TextStyle* kNormalStyle = new TextStyle(); - - if (sort_style_ranges_) { - sort_style_ranges_ = false; - std::sort(style_ranges_.begin(), style_ranges_.end(), - TextStyleRangeComparator); - } - - // If a user is compositing text, use composition's style. - // TODO(oshima): ask suzhe for expected behavior. - const TextStyleRanges& ranges = composition_style_ranges_.size() > 0 ? - composition_style_ranges_ : style_ranges_; - TextStyleRanges::const_iterator next_ = ranges.begin(); - - DCHECK(fragments); - fragments->clear(); - size_t current = 0; - size_t end = text_.length(); - while(next_ != ranges.end()) { - const TextStyleRange* text_style_range = *next_++; - const ui::Range& range = text_style_range->range; - const TextStyle* style = text_style_range->style; - - DCHECK(!range.is_empty()); - DCHECK(range.IsValid()); - if (range.is_empty() || !range.IsValid()) - continue; - - size_t start = std::min(range.start(), end); - - if (start == end) // Exit loop if it reached the end. - break; - else if (current < start) // Fill the gap to next style with normal text. - fragments->push_back(TextFragment(current, start, kNormalStyle)); - - current = std::min(range.end(), end); - fragments->push_back(TextFragment(start, current, style)); - } - // If there is any text left add it as normal text. - if (current != end) - fragments->push_back(TextFragment(current, end, kNormalStyle)); +const string16& TextfieldViewsModel::GetText() const { + return render_text_->text(); } bool TextfieldViewsModel::SetText(const string16& text) { @@ -455,10 +296,10 @@ bool TextfieldViewsModel::SetText(const string16& text) { ConfirmCompositionText(); changed = true; } - if (text_ != text) { + if (GetText() != text) { if (changed) // No need to remember composition. Undo(); - size_t old_cursor = cursor_pos_; + size_t old_cursor = GetCursorPosition(); size_t new_cursor = old_cursor > text.length() ? text.length() : old_cursor; SelectAll(); // If there is a composition text, don't merge with previous edit. @@ -469,7 +310,7 @@ bool TextfieldViewsModel::SetText(const string16& text) { new_cursor, text, 0U); - cursor_pos_ = new_cursor; + render_text_->SetCursorPosition(new_cursor); } ClearSelection(); return changed; @@ -478,10 +319,10 @@ bool TextfieldViewsModel::SetText(const string16& text) { void TextfieldViewsModel::Append(const string16& text) { if (HasCompositionText()) ConfirmCompositionText(); - size_t save = cursor_pos_; - MoveCursorToEnd(false); + size_t save = GetCursorPosition(); + MoveCursorRight(gfx::LINE_BREAK, false); InsertText(text); - cursor_pos_ = save; + render_text_->SetCursorPosition(save); ClearSelection(); } @@ -495,8 +336,9 @@ bool TextfieldViewsModel::Delete() { DeleteSelection(); return true; } - if (text_.length() > cursor_pos_) { - ExecuteAndRecordDelete(cursor_pos_, cursor_pos_ + 1, true); + if (GetText().length() > GetCursorPosition()) { + size_t cursor_position = GetCursorPosition(); + ExecuteAndRecordDelete(cursor_position, cursor_position + 1, true); return true; } return false; @@ -512,194 +354,79 @@ bool TextfieldViewsModel::Backspace() { DeleteSelection(); return true; } - if (cursor_pos_ > 0) { - ExecuteAndRecordDelete(cursor_pos_, cursor_pos_ - 1, true); + if (GetCursorPosition() > 0) { + size_t cursor_position = GetCursorPosition(); + ExecuteAndRecordDelete(cursor_position, cursor_position - 1, true); return true; } return false; } -void TextfieldViewsModel::MoveCursorLeft(bool select) { - if (HasCompositionText()) - ConfirmCompositionText(); - // TODO(oshima): support BIDI - if (select) { - if (cursor_pos_ > 0) - cursor_pos_--; - } else { - if (HasSelection()) - cursor_pos_ = std::min(cursor_pos_, selection_start_); - else if (cursor_pos_ > 0) - cursor_pos_--; - ClearSelection(); - } +size_t TextfieldViewsModel::GetCursorPosition() const { + return render_text_->GetCursorPosition(); } -void TextfieldViewsModel::MoveCursorRight(bool select) { +void TextfieldViewsModel::MoveCursorLeft(gfx::BreakType break_type, + bool select) { if (HasCompositionText()) ConfirmCompositionText(); - // TODO(oshima): support BIDI - if (select) { - cursor_pos_ = std::min(text_.length(), cursor_pos_ + 1); - } else { - if (HasSelection()) - cursor_pos_ = std::max(cursor_pos_, selection_start_); - else - cursor_pos_ = std::min(text_.length(), cursor_pos_ + 1); - ClearSelection(); - } + render_text_->MoveCursorLeft(break_type, select); } -void TextfieldViewsModel::MoveCursorToPreviousWord(bool select) { +void TextfieldViewsModel::MoveCursorRight(gfx::BreakType break_type, + bool select) { if (HasCompositionText()) ConfirmCompositionText(); - // Notes: We always iterate words from the begining. - // This is probably fast enough for our usage, but we may - // want to modify WordIterator so that it can start from the - // middle of string and advance backwards. - base::i18n::BreakIterator iter(text_, base::i18n::BreakIterator::BREAK_WORD); - bool success = iter.Init(); - DCHECK(success); - if (!success) - return; - int last = 0; - while (iter.Advance()) { - if (iter.IsWord()) { - size_t begin = iter.pos() - iter.GetString().length(); - if (begin == cursor_pos_) { - // The cursor is at the beginning of a word. - // Move to previous word. - break; - } else if(iter.pos() >= cursor_pos_) { - // The cursor is in the middle or at the end of a word. - // Move to the top of current word. - last = begin; - break; - } else { - last = iter.pos() - iter.GetString().length(); - } - } - } - - cursor_pos_ = last; - if (!select) - ClearSelection(); + render_text_->MoveCursorRight(break_type, select); } -void TextfieldViewsModel::MoveCursorToNextWord(bool select) { +bool TextfieldViewsModel::MoveCursorTo(size_t pos, bool select) { if (HasCompositionText()) ConfirmCompositionText(); - base::i18n::BreakIterator iter(text_, base::i18n::BreakIterator::BREAK_WORD); - bool success = iter.Init(); - DCHECK(success); - if (!success) - return; - size_t pos = 0; - while (iter.Advance()) { - pos = iter.pos(); - if (iter.IsWord() && pos > cursor_pos_) { - break; - } - } - cursor_pos_ = pos; - if (!select) - ClearSelection(); -} - -void TextfieldViewsModel::MoveCursorToHome(bool select) { - MoveCursorTo(0, select); -} - -void TextfieldViewsModel::MoveCursorToEnd(bool select) { - MoveCursorTo(text_.length(), select); + return render_text_->MoveCursorTo(pos, select); } -bool TextfieldViewsModel::MoveCursorTo(size_t pos, bool select) { +bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) { if (HasCompositionText()) ConfirmCompositionText(); - bool changed = cursor_pos_ != pos || select != HasSelection(); - cursor_pos_ = pos; - if (!select) - ClearSelection(); - return changed; + return render_text_->MoveCursorTo(point, select); } -gfx::Rect TextfieldViewsModel::GetSelectionBounds(const gfx::Font& font) const { - if (!HasSelection()) - return gfx::Rect(); - size_t start = std::min(selection_start_, cursor_pos_); - size_t end = std::max(selection_start_, cursor_pos_); - int start_x = font.GetStringWidth(text_.substr(0, start)); - int end_x = font.GetStringWidth(text_.substr(0, end)); - return gfx::Rect(start_x, 0, end_x - start_x, font.GetHeight()); +std::vector<gfx::Rect> TextfieldViewsModel::GetSelectionBounds() const { + return render_text_->GetSubstringBounds(render_text_->GetSelection()); } string16 TextfieldViewsModel::GetSelectedText() const { - return text_.substr( - std::min(cursor_pos_, selection_start_), - std::abs(static_cast<long>(cursor_pos_ - selection_start_))); + ui::Range selection = render_text_->GetSelection(); + return GetText().substr(selection.GetMin(), selection.length()); } void TextfieldViewsModel::GetSelectedRange(ui::Range* range) const { - *range = ui::Range(selection_start_, cursor_pos_); + *range = render_text_->GetSelection(); } void TextfieldViewsModel::SelectRange(const ui::Range& range) { if (HasCompositionText()) ConfirmCompositionText(); - selection_start_ = GetSafePosition(range.start()); - cursor_pos_ = GetSafePosition(range.end()); + render_text_->SetSelection(range); } void TextfieldViewsModel::SelectAll() { if (HasCompositionText()) ConfirmCompositionText(); - // SelectAll selects towards the end. - cursor_pos_ = text_.length(); - selection_start_ = 0; + render_text_->SelectAll(); } void TextfieldViewsModel::SelectWord() { if (HasCompositionText()) ConfirmCompositionText(); - // First we setup selection_start_ and cursor_pos_. There are so many cases - // because we try to emulate what select-word looks like in a gtk textfield. - // See associated testcase for different cases. - if (cursor_pos_ > 0 && cursor_pos_ < text_.length()) { - if (isalnum(text_[cursor_pos_])) { - selection_start_ = cursor_pos_; - cursor_pos_++; - } else - selection_start_ = cursor_pos_ - 1; - } else if (cursor_pos_ == 0) { - selection_start_ = cursor_pos_; - if (text_.length() > 0) - cursor_pos_++; - } else { - selection_start_ = cursor_pos_ - 1; - } - - // Now we move selection_start_ to beginning of selection. Selection boundary - // is defined as the position where we have alpha-num character on one side - // and non-alpha-num char on the other side. - for (; selection_start_ > 0; selection_start_--) { - if (IsPositionAtWordSelectionBoundary(selection_start_)) - break; - } - - // Now we move cursor_pos_ to end of selection. Selection boundary - // is defined as the position where we have alpha-num character on one side - // and non-alpha-num char on the other side. - for (; cursor_pos_ < text_.length(); cursor_pos_++) { - if (IsPositionAtWordSelectionBoundary(cursor_pos_)) - break; - } + render_text_->SelectWord(); } void TextfieldViewsModel::ClearSelection() { if (HasCompositionText()) ConfirmCompositionText(); - selection_start_ = cursor_pos_; + render_text_->ClearSelection(); } bool TextfieldViewsModel::CanUndo() { @@ -723,8 +450,8 @@ bool TextfieldViewsModel::Undo() { if (HasCompositionText()) // safe guard for release build. CancelCompositionText(); - string16 old = text_; - size_t old_cursor = cursor_pos_; + string16 old = GetText(); + size_t old_cursor = GetCursorPosition(); (*current_edit_)->Commit(); (*current_edit_)->Undo(this); @@ -732,7 +459,7 @@ bool TextfieldViewsModel::Undo() { current_edit_ = edit_history_.end(); else current_edit_--; - return old != text_ || old_cursor != cursor_pos_; + return old != GetText() || old_cursor != GetCursorPosition(); } bool TextfieldViewsModel::Redo() { @@ -746,10 +473,14 @@ bool TextfieldViewsModel::Redo() { current_edit_ = edit_history_.begin(); else current_edit_ ++; - string16 old = text_; - size_t old_cursor = cursor_pos_; + string16 old = GetText(); + size_t old_cursor = GetCursorPosition(); (*current_edit_)->Redo(this); - return old != text_ || old_cursor != cursor_pos_; + return old != GetText() || old_cursor != GetCursorPosition(); +} + +string16 TextfieldViewsModel::GetVisibleText() const { + return GetVisibleText(0U, GetText().length()); } bool TextfieldViewsModel::Cut() { @@ -761,7 +492,8 @@ bool TextfieldViewsModel::Cut() { // than beginning, unlike Delete/Backspace. // TODO(oshima): Change Delete/Backspace to use DeleteSelection, // update DeleteEdit and remove this trick. - std::swap(cursor_pos_, selection_start_); + ui::Range selection = render_text_->GetSelection(); + render_text_->SetSelection(ui::Range(selection.end(), selection.start())); DeleteSelection(); return true; } @@ -787,13 +519,14 @@ bool TextfieldViewsModel::Paste() { } bool TextfieldViewsModel::HasSelection() const { - return selection_start_ != cursor_pos_; + return !render_text_->GetSelection().is_empty(); } void TextfieldViewsModel::DeleteSelection() { DCHECK(!HasCompositionText()); DCHECK(HasSelection()); - ExecuteAndRecordDelete(selection_start_, cursor_pos_, false); + ui::Range selection = render_text_->GetSelection(); + ExecuteAndRecordDelete(selection.start(), selection.end(), false); } void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( @@ -801,26 +534,24 @@ void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( if (HasCompositionText()) CancelCompositionText(); ExecuteAndRecordReplace(DO_NOT_MERGE, - cursor_pos_, + GetCursorPosition(), position + text.length(), text, position); } string16 TextfieldViewsModel::GetTextFromRange(const ui::Range& range) const { - if (range.IsValid() && range.GetMin() < text_.length()) - return text_.substr(range.GetMin(), range.length()); + if (range.IsValid() && range.GetMin() < GetText().length()) + return GetText().substr(range.GetMin(), range.length()); return string16(); } void TextfieldViewsModel::GetTextRange(ui::Range* range) const { - *range = ui::Range(0, text_.length()); + *range = ui::Range(0, GetText().length()); } void TextfieldViewsModel::SetCompositionText( const ui::CompositionText& composition) { - static const TextStyle* composition_style = CreateUnderlineStyle(); - if (HasCompositionText()) CancelCompositionText(); else if (HasSelection()) @@ -829,95 +560,55 @@ void TextfieldViewsModel::SetCompositionText( if (composition.text.empty()) return; - size_t length = composition.text.length(); - text_.insert(cursor_pos_, composition.text); - composition_start_ = cursor_pos_; - composition_end_ = composition_start_ + length; - for (ui::CompositionUnderlines::const_iterator iter = - composition.underlines.begin(); - iter != composition.underlines.end(); - iter++) { - size_t start = composition_start_ + iter->start_offset; - size_t end = composition_start_ + iter->end_offset; - InsertStyle(&composition_style_ranges_, - new TextStyleRange(composition_style, start, end)); - } - std::sort(composition_style_ranges_.begin(), - composition_style_ranges_.end(), TextStyleRangeComparator); - - if (composition.selection.IsValid()) { - selection_start_ = - std::min(composition_start_ + composition.selection.start(), - composition_end_); - cursor_pos_ = - std::min(composition_start_ + composition.selection.end(), - composition_end_); - } else { - cursor_pos_ = composition_end_; - ClearSelection(); - } + size_t cursor = GetCursorPosition(); + string16 new_text = GetText(); + render_text_->SetText(new_text.insert(cursor, composition.text)); + ui::Range range(cursor, cursor + composition.text.length()); + render_text_->SetCompositionRange(range); + // TODO(msw): Support multiple composition underline ranges. + + if (composition.selection.IsValid()) + render_text_->SetSelection(ui::Range( + std::min(range.start() + composition.selection.start(), range.end()), + std::min(range.start() + composition.selection.end(), range.end()))); + else + render_text_->SetCursorPosition(range.end()); } void TextfieldViewsModel::ConfirmCompositionText() { DCHECK(HasCompositionText()); - string16 new_text = - text_.substr(composition_start_, composition_end_ - composition_start_); + ui::Range range = render_text_->GetCompositionRange(); + string16 text = GetText().substr(range.start(), range.length()); // TODO(oshima): current behavior on ChromeOS is a bit weird and not // sure exactly how this should work. Find out and fix if necessary. - AddOrMergeEditHistory(new InsertEdit(false, new_text, composition_start_)); - cursor_pos_ = composition_end_; + AddOrMergeEditHistory(new InsertEdit(false, text, range.start())); + render_text_->SetCursorPosition(range.end()); ClearComposition(); - ClearSelection(); if (delegate_) delegate_->OnCompositionTextConfirmedOrCleared(); } void TextfieldViewsModel::CancelCompositionText() { DCHECK(HasCompositionText()); - text_.erase(composition_start_, composition_end_ - composition_start_); - cursor_pos_ = composition_start_; + ui::Range range = render_text_->GetCompositionRange(); + string16 new_text = GetText(); + render_text_->SetText(new_text.erase(range.start(), range.length())); + render_text_->SetCursorPosition(range.start()); ClearComposition(); - ClearSelection(); if (delegate_) delegate_->OnCompositionTextConfirmedOrCleared(); } void TextfieldViewsModel::ClearComposition() { - composition_start_ = composition_end_ = string16::npos; - STLDeleteContainerPointers(composition_style_ranges_.begin(), - composition_style_ranges_.end()); - composition_style_ranges_.clear(); -} - -void TextfieldViewsModel::ApplyTextStyle(const TextStyle* style, - const ui::Range& range) { - TextStyleRange* new_text_style_range = range.is_reversed() ? - new TextStyleRange(style, ui::Range(range.end(), range.start())) : - new TextStyleRange(style, range); - InsertStyle(&style_ranges_, new_text_style_range); - sort_style_ranges_ = true; + render_text_->SetCompositionRange(ui::Range::InvalidRange()); } void TextfieldViewsModel::GetCompositionTextRange(ui::Range* range) const { - if (HasCompositionText()) - *range = ui::Range(composition_start_, composition_end_); - else - *range = ui::Range::InvalidRange(); + *range = ui::Range(render_text_->GetCompositionRange()); } bool TextfieldViewsModel::HasCompositionText() const { - return composition_start_ != composition_end_; -} - -TextStyle* TextfieldViewsModel::CreateTextStyle() { - TextStyle* style = new TextStyle(); - text_styles_.push_back(style); - return style; -} - -void TextfieldViewsModel::ClearAllTextStyles() { - STLDeleteContainerPointers(style_ranges_.begin(), style_ranges_.end()); - style_ranges_.clear(); + return !render_text_->GetCompositionRange().is_empty(); } ///////////////////////////////////////////////////////////////// @@ -927,19 +618,7 @@ string16 TextfieldViewsModel::GetVisibleText(size_t begin, size_t end) const { DCHECK(end >= begin); if (is_password_) return string16(end - begin, '*'); - return text_.substr(begin, end - begin); -} - -bool TextfieldViewsModel::IsPositionAtWordSelectionBoundary(size_t pos) { - return (isalnum(text_[pos - 1]) && !isalnum(text_[pos])) || - (!isalnum(text_[pos - 1]) && isalnum(text_[pos])); -} - -size_t TextfieldViewsModel::GetSafePosition(size_t position) const { - if (position > text_.length()) { - return text_.length(); - } - return position; + return GetText().substr(begin, end - begin); } void TextfieldViewsModel::InsertTextInternal(const string16& text, @@ -957,10 +636,12 @@ void TextfieldViewsModel::InsertTextInternal(const string16& text, void TextfieldViewsModel::ReplaceTextInternal(const string16& text, bool mergeable) { - if (HasCompositionText()) + if (HasCompositionText()) { CancelCompositionText(); - else if (!HasSelection()) - SelectRange(ui::Range(cursor_pos_ + text.length(), cursor_pos_)); + } else if (!HasSelection()) { + size_t cursor = GetCursorPosition(); + render_text_->SetSelection(ui::Range(cursor + text.length(), cursor)); + } // Edit history is recorded in InsertText. InsertTextInternal(text, mergeable); } @@ -989,7 +670,7 @@ void TextfieldViewsModel::ExecuteAndRecordDelete(size_t from, size_t to, bool mergeable) { size_t old_text_start = std::min(from, to); - const string16 text = text_.substr(old_text_start, + const string16 text = GetText().substr(old_text_start, std::abs(static_cast<long>(from - to))); bool backward = from > to; Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); @@ -1001,10 +682,10 @@ void TextfieldViewsModel::ExecuteAndRecordDelete(size_t from, void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( MergeType merge_type, const string16& new_text) { - size_t new_text_start = std::min(cursor_pos_, selection_start_); + size_t new_text_start = render_text_->GetSelection().GetMin(); size_t new_cursor_pos = new_text_start + new_text.length(); ExecuteAndRecordReplace(merge_type, - cursor_pos_, + GetCursorPosition(), new_cursor_pos, new_text, new_text_start); @@ -1015,8 +696,8 @@ void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, size_t new_cursor_pos, const string16& new_text, size_t new_text_start) { - size_t old_text_start = std::min(cursor_pos_, selection_start_); - bool backward = selection_start_ > cursor_pos_; + size_t old_text_start = render_text_->GetSelection().GetMin(); + bool backward = render_text_->GetSelection().is_reversed(); Edit* edit = new ReplaceEdit(merge_type, GetSelectedText(), old_cursor_pos, @@ -1033,7 +714,7 @@ void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, void TextfieldViewsModel::ExecuteAndRecordInsert(const string16& text, bool mergeable) { - Edit* edit = new InsertEdit(mergeable, text, cursor_pos_); + Edit* edit = new InsertEdit(mergeable, text, GetCursorPosition()); bool delete_edit = AddOrMergeEditHistory(edit); edit->Redo(this); if (delete_edit) @@ -1067,21 +748,14 @@ void TextfieldViewsModel::ModifyText(size_t delete_from, size_t new_text_insert_at, size_t new_cursor_pos) { DCHECK_LE(delete_from, delete_to); + string16 text = GetText(); if (delete_from != delete_to) - text_.erase(delete_from, delete_to - delete_from); + render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); if (!new_text.empty()) - text_.insert(new_text_insert_at, new_text); - cursor_pos_ = new_cursor_pos; - ClearSelection(); + render_text_->SetText(text.insert(new_text_insert_at, new_text)); + render_text_->SetCursorPosition(new_cursor_pos); // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). // This looks fine feature and we may want to do the same. } -// static -TextStyle* TextfieldViewsModel::CreateUnderlineStyle() { - TextStyle* style = new TextStyle(); - style->set_underline(true); - return style; -} - } // namespace views diff --git a/views/controls/textfield/textfield_views_model.h b/views/controls/textfield/textfield_views_model.h index 297c8ae..3f63fe4 100644 --- a/views/controls/textfield/textfield_views_model.h +++ b/views/controls/textfield/textfield_views_model.h @@ -10,13 +10,18 @@ #include <vector> #include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" #include "base/string16.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/ime/composition_text.h" #include "ui/gfx/rect.h" +#include "ui/gfx/render_text.h" namespace gfx { +class Canvas; class Font; +class RenderText; +struct StyleRange; } // namespace gfx namespace ui { @@ -25,15 +30,10 @@ class Range; namespace views { -class TextStyle; -typedef std::vector<TextStyle*> TextStyles; - namespace internal { // Internal Edit class that keeps track of edits for undo/redo. class Edit; -struct TextStyleRange; - // C++ doesn't allow forward decl enum, so let's define here. enum MergeType { // The edit should not be merged with next edit. It still may @@ -49,8 +49,6 @@ enum MergeType { } // namespace internal -typedef std::vector<internal::TextStyleRange*> TextStyleRanges; - // A model that represents a text content for TextfieldViews. // It supports editing, selection and cursor manipulation. class TextfieldViewsModel { @@ -70,31 +68,14 @@ class TextfieldViewsModel { explicit TextfieldViewsModel(Delegate* delegate); virtual ~TextfieldViewsModel(); - // Text fragment info. Used to draw selected text. - // We may replace this with TextAttribute class - // in the future to support multi-color text - // for omnibox. - struct TextFragment { - TextFragment(size_t start, size_t end, const views::TextStyle* s) - : range(start, end), style(s) { - } - // The start and end position of text fragment. - ui::Range range; - const TextStyle* style; - }; - typedef std::vector<TextFragment> TextFragments; - - // Gets the text element info. - void GetFragments(TextFragments* elements); - void set_is_password(bool is_password) { is_password_ = is_password; } - const string16& text() const { return text_; } // Edit related methods. - // Sest the text. Returns true if the text has been modified. The + const string16& GetText() const; + // Sets the text. Returns true if the text has been modified. The // current composition text will be confirmed first. Setting // the same text will not add edit history because it's not user // visible change nor user-initiated change. This allow a client @@ -102,6 +83,8 @@ class TextfieldViewsModel { // messing edit history. bool SetText(const string16& text); + gfx::RenderText* render_text() { return render_text_.get(); } + // Inserts given |text| at the current cursor position. // The current composition text will be cleared. void InsertText(const string16& text) { @@ -143,46 +126,23 @@ class TextfieldViewsModel { // Cursor related methods. // Returns the current cursor position. - size_t cursor_pos() const { return cursor_pos_; } - - // Moves the cursor left by one position (as if, the user has pressed the left - // arrow key). If |select| is true, it updates the selection accordingly. - // The current composition text will be confirmed. - void MoveCursorLeft(bool select); + size_t GetCursorPosition() const; - // Moves the cursor right by one position (as if, the user has pressed the - // right arrow key). If |select| is true, it updates the selection - // accordingly. - // The current composition text will be confirmed. - void MoveCursorRight(bool select); - - // Moves the cursor left by one word (word boundry is defined by space). - // If |select| is true, it updates the selection accordingly. - // The current composition text will be confirmed. - void MoveCursorToPreviousWord(bool select); - - // Moves the cursor right by one word (word boundry is defined by space). - // If |select| is true, it updates the selection accordingly. - // The current composition text will be confirmed. - void MoveCursorToNextWord(bool select); - - // Moves the cursor to start of the textfield contents. - // If |select| is true, it updates the selection accordingly. + // Moves the cursor, see RenderText for additional details. // The current composition text will be confirmed. - void MoveCursorToHome(bool select); - - // Moves the cursor to end of the textfield contents. - // If |select| is true, it updates the selection accordingly. - // The current composition text will be confirmed. - void MoveCursorToEnd(bool select); + void MoveCursorLeft(gfx::BreakType break_type, bool select); + void MoveCursorRight(gfx::BreakType break_type, bool select); // Moves the cursor to the specified |position|. // If |select| is true, it updates the selection accordingly. // The current composition text will be confirmed. bool MoveCursorTo(size_t position, bool select); + // Helper function to call MoveCursorTo on the TextfieldViewsModel. + bool MoveCursorTo(const gfx::Point& point, bool select); + // Returns the bounds of selected text. - gfx::Rect GetSelectionBounds(const gfx::Font& font) const; + std::vector<gfx::Rect> GetSelectionBounds() const; // Selection related method @@ -223,9 +183,7 @@ class TextfieldViewsModel { // Returns visible text. If the field is password, it returns the // sequence of "*". - string16 GetVisibleText() const { - return GetVisibleText(0U, text_.length()); - } + string16 GetVisibleText() const; // Cuts the currently selected text and puts it to clipboard. Returns true // if text has changed after cutting. @@ -277,15 +235,10 @@ class TextfieldViewsModel { // Returns true if there is composition text. bool HasCompositionText() const; - TextStyle* CreateTextStyle(); - - void ClearAllTextStyles(); - private: friend class NativeTextfieldViews; friend class NativeTextfieldViewsTest; friend class TextfieldViewsModelTest; - friend class TextStyle; friend class UndoRedo_BasicTest; friend class UndoRedo_CutCopyPasteTest; friend class UndoRedo_ReplaceTest; @@ -294,18 +247,10 @@ class TextfieldViewsModel { FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_BasicTest); FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_CutCopyPasteTest); FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_ReplaceTest); - FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, TextStyleTest); // Returns the visible text given |start| and |end|. string16 GetVisibleText(size_t start, size_t end) const; - // Utility for SelectWord(). Checks whether position pos is at word boundary. - bool IsPositionAtWordSelectionBoundary(size_t pos); - - // Returns the normalized cursor position that does not exceed the - // text length. - size_t GetSafePosition(size_t position) const; - // Insert the given |text|. |mergeable| indicates if this insert // operation can be merged to previous edit in the edit history. void InsertTextInternal(const string16& text, bool mergeable); @@ -349,26 +294,12 @@ class TextfieldViewsModel { void ClearComposition(); - void ApplyTextStyle(const TextStyle* style, const ui::Range& range); - - static TextStyle* CreateUnderlineStyle(); - // Pointer to a TextfieldViewsModel::Delegate instance, should be provided by // the View object. Delegate* delegate_; - // The text in utf16 format. - string16 text_; - - // Current cursor position. - size_t cursor_pos_; - - // Selection range. - size_t selection_start_; - - // Composition text range. - size_t composition_start_; - size_t composition_end_; + // The stylized text, cursor, selection, and the visual layout model. + scoped_ptr<gfx::RenderText> render_text_; // True if the text is the password. bool is_password_; @@ -389,19 +320,6 @@ class TextfieldViewsModel { // 3) redone all undone edits. EditHistory::iterator current_edit_; - // This manages all styles objects. - TextStyles text_styles_; - - // List of style ranges. Elements in the list never overlap each other. - // Elements are not sorted at the time of insertion, and gets sorted - // when it's painted (if necessary). - TextStyleRanges style_ranges_; - // True if the style_ranges_ needs to be sorted. - bool sort_style_ranges_; - - // List of style ranges for composition text. - TextStyleRanges composition_style_ranges_; - DISALLOW_COPY_AND_ASSIGN(TextfieldViewsModel); }; diff --git a/views/controls/textfield/textfield_views_model_unittest.cc b/views/controls/textfield/textfield_views_model_unittest.cc index 7e2f7e6..907e353 100644 --- a/views/controls/textfield/textfield_views_model_unittest.cc +++ b/views/controls/textfield/textfield_views_model_unittest.cc @@ -10,7 +10,7 @@ #include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/base/range/range.h" -#include "views/controls/textfield/text_style.h" +#include "ui/gfx/render_text.h" #include "views/controls/textfield/textfield.h" #include "views/controls/textfield/textfield_views_model.h" #include "views/test/test_views_delegate.h" @@ -19,8 +19,6 @@ namespace views { -#include "views/test/views_test_base.h" - class TextfieldViewsModelTest : public ViewsTestBase, public TextfieldViewsModel::Delegate { public: @@ -47,57 +45,56 @@ class TextfieldViewsModelTest : public ViewsTestBase, #define EXPECT_STR_EQ(ascii, utf16) \ EXPECT_EQ(ASCIIToWide(ascii), UTF16ToWide(utf16)) - TEST_F(TextfieldViewsModelTest, EditString) { TextfieldViewsModel model(NULL); // append two strings model.Append(ASCIIToUTF16("HILL")); - EXPECT_STR_EQ("HILL", model.text()); + EXPECT_STR_EQ("HILL", model.GetText()); model.Append(ASCIIToUTF16("WORLD")); - EXPECT_STR_EQ("HILLWORLD", model.text()); + EXPECT_STR_EQ("HILLWORLD", model.GetText()); // Insert "E" to make hello - model.MoveCursorRight(false); + model.MoveCursorRight(gfx::CHARACTER_BREAK, false); model.InsertChar('E'); - EXPECT_STR_EQ("HEILLWORLD", model.text()); + EXPECT_STR_EQ("HEILLWORLD", model.GetText()); // Replace "I" with "L" model.ReplaceChar('L'); - EXPECT_STR_EQ("HELLLWORLD", model.text()); + EXPECT_STR_EQ("HELLLWORLD", model.GetText()); model.ReplaceChar('L'); model.ReplaceChar('O'); - EXPECT_STR_EQ("HELLOWORLD", model.text()); + EXPECT_STR_EQ("HELLOWORLD", model.GetText()); // Delete 6th char "W", then delete 5th char O" - EXPECT_EQ(5U, model.cursor_pos()); + EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_TRUE(model.Delete()); - EXPECT_STR_EQ("HELLOORLD", model.text()); + EXPECT_STR_EQ("HELLOORLD", model.GetText()); EXPECT_TRUE(model.Backspace()); - EXPECT_EQ(4U, model.cursor_pos()); - EXPECT_STR_EQ("HELLORLD", model.text()); + EXPECT_EQ(4U, model.GetCursorPosition()); + EXPECT_STR_EQ("HELLORLD", model.GetText()); // Move the cursor to start. backspace should fail. - model.MoveCursorToHome(false); + model.MoveCursorLeft(gfx::LINE_BREAK, false); EXPECT_FALSE(model.Backspace()); - EXPECT_STR_EQ("HELLORLD", model.text()); + EXPECT_STR_EQ("HELLORLD", model.GetText()); // Move the cursor to the end. delete should fail. - model.MoveCursorToEnd(false); + model.MoveCursorRight(gfx::LINE_BREAK, false); EXPECT_FALSE(model.Delete()); - EXPECT_STR_EQ("HELLORLD", model.text()); + EXPECT_STR_EQ("HELLORLD", model.GetText()); // but backspace should work. EXPECT_TRUE(model.Backspace()); - EXPECT_STR_EQ("HELLORL", model.text()); + EXPECT_STR_EQ("HELLORL", model.GetText()); } TEST_F(TextfieldViewsModelTest, EmptyString) { TextfieldViewsModel model(NULL); - EXPECT_EQ(string16(), model.text()); + EXPECT_EQ(string16(), model.GetText()); EXPECT_EQ(string16(), model.GetSelectedText()); EXPECT_EQ(string16(), model.GetVisibleText()); - model.MoveCursorLeft(true); - EXPECT_EQ(0U, model.cursor_pos()); - model.MoveCursorRight(true); - EXPECT_EQ(0U, model.cursor_pos()); + model.MoveCursorLeft(gfx::CHARACTER_BREAK, true); + EXPECT_EQ(0U, model.GetCursorPosition()); + model.MoveCursorRight(gfx::CHARACTER_BREAK, true); + EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_EQ(string16(), model.GetSelectedText()); @@ -108,15 +105,15 @@ TEST_F(TextfieldViewsModelTest, EmptyString) { TEST_F(TextfieldViewsModelTest, Selection) { TextfieldViewsModel model(NULL); model.Append(ASCIIToUTF16("HELLO")); - model.MoveCursorRight(false); - model.MoveCursorRight(true); + model.MoveCursorRight(gfx::CHARACTER_BREAK, false); + model.MoveCursorRight(gfx::CHARACTER_BREAK, true); EXPECT_STR_EQ("E", model.GetSelectedText()); - model.MoveCursorRight(true); + model.MoveCursorRight(gfx::CHARACTER_BREAK, true); EXPECT_STR_EQ("EL", model.GetSelectedText()); - model.MoveCursorToHome(true); + model.MoveCursorLeft(gfx::LINE_BREAK, true); EXPECT_STR_EQ("H", model.GetSelectedText()); - model.MoveCursorToEnd(true); + model.MoveCursorRight(gfx::LINE_BREAK, true); EXPECT_STR_EQ("ELLO", model.GetSelectedText()); model.ClearSelection(); EXPECT_EQ(string16(), model.GetSelectedText()); @@ -132,49 +129,49 @@ TEST_F(TextfieldViewsModelTest, Selection) { model.MoveCursorTo(1U, false); model.MoveCursorTo(3U, true); EXPECT_STR_EQ("EL", model.GetSelectedText()); - model.MoveCursorLeft(false); - EXPECT_EQ(1U, model.cursor_pos()); + model.MoveCursorLeft(gfx::CHARACTER_BREAK, false); + EXPECT_EQ(1U, model.GetCursorPosition()); model.MoveCursorTo(1U, false); model.MoveCursorTo(3U, true); - model.MoveCursorRight(false); - EXPECT_EQ(3U, model.cursor_pos()); + model.MoveCursorRight(gfx::CHARACTER_BREAK, false); + EXPECT_EQ(3U, model.GetCursorPosition()); // Select all and move cursor model.SelectAll(); - model.MoveCursorLeft(false); - EXPECT_EQ(0U, model.cursor_pos()); + model.MoveCursorLeft(gfx::CHARACTER_BREAK, false); + EXPECT_EQ(0U, model.GetCursorPosition()); model.SelectAll(); - model.MoveCursorRight(false); - EXPECT_EQ(5U, model.cursor_pos()); + model.MoveCursorRight(gfx::CHARACTER_BREAK, false); + EXPECT_EQ(5U, model.GetCursorPosition()); } TEST_F(TextfieldViewsModelTest, SelectionAndEdit) { TextfieldViewsModel model(NULL); model.Append(ASCIIToUTF16("HELLO")); - model.MoveCursorRight(false); - model.MoveCursorRight(true); - model.MoveCursorRight(true); // select "EL" + model.MoveCursorRight(gfx::CHARACTER_BREAK, false); + model.MoveCursorRight(gfx::CHARACTER_BREAK, true); + model.MoveCursorRight(gfx::CHARACTER_BREAK, true); // select "EL" EXPECT_TRUE(model.Backspace()); - EXPECT_STR_EQ("HLO", model.text()); + EXPECT_STR_EQ("HLO", model.GetText()); model.Append(ASCIIToUTF16("ILL")); - model.MoveCursorRight(true); - model.MoveCursorRight(true); // select "LO" + model.MoveCursorRight(gfx::CHARACTER_BREAK, true); + model.MoveCursorRight(gfx::CHARACTER_BREAK, true); // select "LO" EXPECT_TRUE(model.Delete()); - EXPECT_STR_EQ("HILL", model.text()); - EXPECT_EQ(1U, model.cursor_pos()); - model.MoveCursorRight(true); // select "I" + EXPECT_STR_EQ("HILL", model.GetText()); + EXPECT_EQ(1U, model.GetCursorPosition()); + model.MoveCursorRight(gfx::CHARACTER_BREAK, true); // select "I" model.InsertChar('E'); - EXPECT_STR_EQ("HELL", model.text()); - model.MoveCursorToHome(false); - model.MoveCursorRight(true); // select "H" + EXPECT_STR_EQ("HELL", model.GetText()); + model.MoveCursorLeft(gfx::LINE_BREAK, false); + model.MoveCursorRight(gfx::CHARACTER_BREAK, true); // select "H" model.ReplaceChar('B'); - EXPECT_STR_EQ("BELL", model.text()); - model.MoveCursorToEnd(false); - model.MoveCursorLeft(true); - model.MoveCursorLeft(true); // select ">LL" + EXPECT_STR_EQ("BELL", model.GetText()); + model.MoveCursorRight(gfx::LINE_BREAK, false); + model.MoveCursorLeft(gfx::CHARACTER_BREAK, true); + model.MoveCursorLeft(gfx::CHARACTER_BREAK, true); // select "ELL" model.ReplaceChar('E'); - EXPECT_STR_EQ("BEE", model.text()); + EXPECT_STR_EQ("BEE", model.GetText()); } TEST_F(TextfieldViewsModelTest, Password) { @@ -182,110 +179,88 @@ TEST_F(TextfieldViewsModelTest, Password) { model.set_is_password(true); model.Append(ASCIIToUTF16("HELLO")); EXPECT_STR_EQ("*****", model.GetVisibleText()); - EXPECT_STR_EQ("HELLO", model.text()); + EXPECT_STR_EQ("HELLO", model.GetText()); EXPECT_TRUE(model.Delete()); EXPECT_STR_EQ("****", model.GetVisibleText()); - EXPECT_STR_EQ("ELLO", model.text()); - EXPECT_EQ(0U, model.cursor_pos()); + EXPECT_STR_EQ("ELLO", model.GetText()); + EXPECT_EQ(0U, model.GetCursorPosition()); model.SelectAll(); EXPECT_STR_EQ("ELLO", model.GetSelectedText()); - EXPECT_EQ(4U, model.cursor_pos()); + EXPECT_EQ(4U, model.GetCursorPosition()); model.InsertChar('X'); EXPECT_STR_EQ("*", model.GetVisibleText()); - EXPECT_STR_EQ("X", model.text()); + EXPECT_STR_EQ("X", model.GetText()); } TEST_F(TextfieldViewsModelTest, Word) { TextfieldViewsModel model(NULL); model.Append( ASCIIToUTF16("The answer to Life, the Universe, and Everything")); - model.MoveCursorToNextWord(false); - EXPECT_EQ(3U, model.cursor_pos()); - model.MoveCursorToNextWord(false); - EXPECT_EQ(10U, model.cursor_pos()); - model.MoveCursorToNextWord(false); - model.MoveCursorToNextWord(false); - EXPECT_EQ(18U, model.cursor_pos()); + model.MoveCursorRight(gfx::WORD_BREAK, false); + EXPECT_EQ(3U, model.GetCursorPosition()); + model.MoveCursorRight(gfx::WORD_BREAK, false); + EXPECT_EQ(10U, model.GetCursorPosition()); + model.MoveCursorRight(gfx::WORD_BREAK, false); + model.MoveCursorRight(gfx::WORD_BREAK, false); + EXPECT_EQ(18U, model.GetCursorPosition()); // Should passes the non word char ',' - model.MoveCursorToNextWord(true); - EXPECT_EQ(23U, model.cursor_pos()); + model.MoveCursorRight(gfx::WORD_BREAK, true); + EXPECT_EQ(23U, model.GetCursorPosition()); EXPECT_STR_EQ(", the", model.GetSelectedText()); // Move to the end. - model.MoveCursorToNextWord(true); - model.MoveCursorToNextWord(true); - model.MoveCursorToNextWord(true); + model.MoveCursorRight(gfx::WORD_BREAK, true); + model.MoveCursorRight(gfx::WORD_BREAK, true); + model.MoveCursorRight(gfx::WORD_BREAK, true); EXPECT_STR_EQ(", the Universe, and Everything", model.GetSelectedText()); // Should be safe to go next word at the end. - model.MoveCursorToNextWord(true); + model.MoveCursorRight(gfx::WORD_BREAK, true); EXPECT_STR_EQ(", the Universe, and Everything", model.GetSelectedText()); model.InsertChar('2'); - EXPECT_EQ(19U, model.cursor_pos()); + EXPECT_EQ(19U, model.GetCursorPosition()); // Now backwards. - model.MoveCursorLeft(false); // leave 2. - model.MoveCursorToPreviousWord(true); - EXPECT_EQ(14U, model.cursor_pos()); + model.MoveCursorLeft(gfx::CHARACTER_BREAK, false); // leave 2. + model.MoveCursorLeft(gfx::WORD_BREAK, true); + EXPECT_EQ(14U, model.GetCursorPosition()); EXPECT_STR_EQ("Life", model.GetSelectedText()); - model.MoveCursorToPreviousWord(true); + model.MoveCursorLeft(gfx::WORD_BREAK, true); EXPECT_STR_EQ("to Life", model.GetSelectedText()); - model.MoveCursorToPreviousWord(true); - model.MoveCursorToPreviousWord(true); - model.MoveCursorToPreviousWord(true); // Select to the begining. + model.MoveCursorLeft(gfx::WORD_BREAK, true); + model.MoveCursorLeft(gfx::WORD_BREAK, true); + model.MoveCursorLeft(gfx::WORD_BREAK, true); // Select to the begining. EXPECT_STR_EQ("The answer to Life", model.GetSelectedText()); // Should be safe to go pervious word at the begining. - model.MoveCursorToPreviousWord(true); + model.MoveCursorLeft(gfx::WORD_BREAK, true); EXPECT_STR_EQ("The answer to Life", model.GetSelectedText()); model.ReplaceChar('4'); EXPECT_EQ(string16(), model.GetSelectedText()); EXPECT_STR_EQ("42", model.GetVisibleText()); } -TEST_F(TextfieldViewsModelTest, TextFragment) { - TextfieldViewsModel model(NULL); - TextfieldViewsModel::TextFragments fragments; - // Empty string has no fragment. - model.GetFragments(&fragments); - EXPECT_EQ(0U, fragments.size()); - - // Some string - model.Append(ASCIIToUTF16("Hello world")); - model.GetFragments(&fragments); - EXPECT_EQ(1U, fragments.size()); - EXPECT_EQ(0U, fragments[0].range.start()); - EXPECT_EQ(11U, fragments[0].range.end()); - - // Selection won't change fragment. - model.MoveCursorToNextWord(true); - model.GetFragments(&fragments); - EXPECT_EQ(1U, fragments.size()); - EXPECT_EQ(0U, fragments[0].range.start()); - EXPECT_EQ(11U, fragments[0].range.end()); -} - TEST_F(TextfieldViewsModelTest, SetText) { TextfieldViewsModel model(NULL); model.Append(ASCIIToUTF16("HELLO")); - model.MoveCursorToEnd(false); + model.MoveCursorRight(gfx::LINE_BREAK, false); model.SetText(ASCIIToUTF16("GOODBYE")); - EXPECT_STR_EQ("GOODBYE", model.text()); + EXPECT_STR_EQ("GOODBYE", model.GetText()); // SetText won't reset the cursor posistion. - EXPECT_EQ(5U, model.cursor_pos()); + EXPECT_EQ(5U, model.GetCursorPosition()); model.SelectAll(); EXPECT_STR_EQ("GOODBYE", model.GetSelectedText()); - model.MoveCursorToEnd(false); - EXPECT_EQ(7U, model.cursor_pos()); + model.MoveCursorRight(gfx::LINE_BREAK, false); + EXPECT_EQ(7U, model.GetCursorPosition()); model.SetText(ASCIIToUTF16("BYE")); // Setting shorter string moves the cursor to the end of the new string. - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_EQ(string16(), model.GetSelectedText()); model.SetText(ASCIIToUTF16("")); - EXPECT_EQ(0U, model.cursor_pos()); + EXPECT_EQ(0U, model.GetCursorPosition()); } TEST_F(TextfieldViewsModelTest, Clipboard) { @@ -296,29 +271,29 @@ TEST_F(TextfieldViewsModelTest, Clipboard) { string16 clipboard_text; TextfieldViewsModel model(NULL); model.Append(ASCIIToUTF16("HELLO WORLD")); - model.MoveCursorToEnd(false); + model.MoveCursorRight(gfx::LINE_BREAK, false); // Test for cut: Empty selection. EXPECT_FALSE(model.Cut()); clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text); EXPECT_STR_EQ(UTF16ToUTF8(initial_clipboard_text), clipboard_text); - EXPECT_STR_EQ("HELLO WORLD", model.text()); - EXPECT_EQ(11U, model.cursor_pos()); + EXPECT_STR_EQ("HELLO WORLD", model.GetText()); + EXPECT_EQ(11U, model.GetCursorPosition()); // Test for cut: Non-empty selection. - model.MoveCursorToPreviousWord(true); + model.MoveCursorLeft(gfx::WORD_BREAK, true); EXPECT_TRUE(model.Cut()); clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text); EXPECT_STR_EQ("WORLD", clipboard_text); - EXPECT_STR_EQ("HELLO ", model.text()); - EXPECT_EQ(6U, model.cursor_pos()); + EXPECT_STR_EQ("HELLO ", model.GetText()); + EXPECT_EQ(6U, model.GetCursorPosition()); // Test for copy: Empty selection. model.Copy(); clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text); EXPECT_STR_EQ("WORLD", clipboard_text); - EXPECT_STR_EQ("HELLO ", model.text()); - EXPECT_EQ(6U, model.cursor_pos()); + EXPECT_STR_EQ("HELLO ", model.GetText()); + EXPECT_EQ(6U, model.GetCursorPosition()); // Test for copy: Non-empty selection. model.Append(ASCIIToUTF16("HELLO WORLD")); @@ -326,24 +301,24 @@ TEST_F(TextfieldViewsModelTest, Clipboard) { model.Copy(); clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text); EXPECT_STR_EQ("HELLO HELLO WORLD", clipboard_text); - EXPECT_STR_EQ("HELLO HELLO WORLD", model.text()); - EXPECT_EQ(17U, model.cursor_pos()); + EXPECT_STR_EQ("HELLO HELLO WORLD", model.GetText()); + EXPECT_EQ(17U, model.GetCursorPosition()); // Test for paste. model.ClearSelection(); - model.MoveCursorToEnd(false); - model.MoveCursorToPreviousWord(true); + model.MoveCursorRight(gfx::LINE_BREAK, false); + model.MoveCursorLeft(gfx::WORD_BREAK, true); EXPECT_TRUE(model.Paste()); clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text); EXPECT_STR_EQ("HELLO HELLO WORLD", clipboard_text); - EXPECT_STR_EQ("HELLO HELLO HELLO HELLO WORLD", model.text()); - EXPECT_EQ(29U, model.cursor_pos()); + EXPECT_STR_EQ("HELLO HELLO HELLO HELLO WORLD", model.GetText()); + EXPECT_EQ(29U, model.GetCursorPosition()); } void SelectWordTestVerifier(TextfieldViewsModel &model, const std::string &expected_selected_string, size_t expected_cursor_pos) { EXPECT_STR_EQ(expected_selected_string, model.GetSelectedText()); - EXPECT_EQ(expected_cursor_pos, model.cursor_pos()); + EXPECT_EQ(expected_cursor_pos, model.GetCursorPosition()); } TEST_F(TextfieldViewsModelTest, SelectWordTest) { @@ -351,7 +326,7 @@ TEST_F(TextfieldViewsModelTest, SelectWordTest) { model.Append(ASCIIToUTF16(" HELLO !! WO RLD ")); // Test when cursor is at the beginning. - model.MoveCursorToHome(false); + model.MoveCursorLeft(gfx::LINE_BREAK, false); model.SelectWord(); SelectWordTestVerifier(model, " ", 2U); @@ -378,7 +353,7 @@ TEST_F(TextfieldViewsModelTest, SelectWordTest) { SelectWordTestVerifier(model, " ", 20U); // Test when cursor is at the end. - model.MoveCursorToEnd(false); + model.MoveCursorRight(gfx::LINE_BREAK, false); model.SelectWord(); SelectWordTestVerifier(model, " ", 24U); } @@ -386,61 +361,61 @@ TEST_F(TextfieldViewsModelTest, SelectWordTest) { TEST_F(TextfieldViewsModelTest, RangeTest) { TextfieldViewsModel model(NULL); model.Append(ASCIIToUTF16("HELLO WORLD")); - model.MoveCursorToHome(false); + model.MoveCursorLeft(gfx::LINE_BREAK, false); ui::Range range; model.GetSelectedRange(&range); EXPECT_TRUE(range.is_empty()); EXPECT_EQ(0U, range.start()); EXPECT_EQ(0U, range.end()); - model.MoveCursorToNextWord(true); + model.MoveCursorRight(gfx::WORD_BREAK, true); model.GetSelectedRange(&range); EXPECT_FALSE(range.is_empty()); EXPECT_FALSE(range.is_reversed()); EXPECT_EQ(0U, range.start()); EXPECT_EQ(5U, range.end()); - model.MoveCursorLeft(true); + model.MoveCursorLeft(gfx::CHARACTER_BREAK, true); model.GetSelectedRange(&range); EXPECT_FALSE(range.is_empty()); EXPECT_EQ(0U, range.start()); EXPECT_EQ(4U, range.end()); - model.MoveCursorToPreviousWord(true); + model.MoveCursorLeft(gfx::WORD_BREAK, true); model.GetSelectedRange(&range); EXPECT_TRUE(range.is_empty()); EXPECT_EQ(0U, range.start()); EXPECT_EQ(0U, range.end()); // now from the end. - model.MoveCursorToEnd(false); + model.MoveCursorRight(gfx::LINE_BREAK, false); model.GetSelectedRange(&range); EXPECT_TRUE(range.is_empty()); EXPECT_EQ(11U, range.start()); EXPECT_EQ(11U, range.end()); - model.MoveCursorToPreviousWord(true); + model.MoveCursorLeft(gfx::WORD_BREAK, true); model.GetSelectedRange(&range); EXPECT_FALSE(range.is_empty()); EXPECT_TRUE(range.is_reversed()); EXPECT_EQ(11U, range.start()); EXPECT_EQ(6U, range.end()); - model.MoveCursorRight(true); + model.MoveCursorRight(gfx::CHARACTER_BREAK, true); model.GetSelectedRange(&range); EXPECT_FALSE(range.is_empty()); EXPECT_TRUE(range.is_reversed()); EXPECT_EQ(11U, range.start()); EXPECT_EQ(7U, range.end()); - model.MoveCursorToNextWord(true); + model.MoveCursorRight(gfx::WORD_BREAK, true); model.GetSelectedRange(&range); EXPECT_TRUE(range.is_empty()); EXPECT_EQ(11U, range.start()); EXPECT_EQ(11U, range.end()); // Select All - model.MoveCursorToHome(true); + model.MoveCursorLeft(gfx::LINE_BREAK, true); model.GetSelectedRange(&range); EXPECT_FALSE(range.is_empty()); EXPECT_TRUE(range.is_reversed()); @@ -497,7 +472,7 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) { model.Append(ASCIIToUTF16("1234590")); model.SelectRange(ui::Range(5, 5)); EXPECT_FALSE(model.HasSelection()); - EXPECT_EQ(5U, model.cursor_pos()); + EXPECT_EQ(5U, model.GetCursorPosition()); ui::Range range; model.GetTextRange(&range); @@ -514,7 +489,7 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) { model.GetTextRange(&range); EXPECT_EQ(10U, range.end()); - EXPECT_STR_EQ("1234567890", model.text()); + EXPECT_STR_EQ("1234567890", model.GetText()); model.GetCompositionTextRange(&range); EXPECT_EQ(5U, range.start()); @@ -527,55 +502,40 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) { EXPECT_EQ(8U, range.end()); EXPECT_STR_EQ("8", model.GetSelectedText()); - TextfieldViewsModel::TextFragments fragments; - model.GetFragments(&fragments); - EXPECT_EQ(3U, fragments.size()); - EXPECT_EQ(0U, fragments[0].range.start()); - EXPECT_EQ(5U, fragments[0].range.end()); - EXPECT_FALSE(fragments[0].style->underline()); - - EXPECT_EQ(5U, fragments[1].range.start()); - EXPECT_EQ(8U, fragments[1].range.end()); - EXPECT_TRUE(fragments[1].style->underline()); - - EXPECT_EQ(8U, fragments[2].range.start()); - EXPECT_EQ(10U, fragments[2].range.end()); - EXPECT_FALSE(fragments[2].style->underline()); - EXPECT_FALSE(composition_text_confirmed_or_cleared_); model.CancelCompositionText(); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; EXPECT_FALSE(model.HasCompositionText()); EXPECT_FALSE(model.HasSelection()); - EXPECT_EQ(5U, model.cursor_pos()); + EXPECT_EQ(5U, model.GetCursorPosition()); model.SetCompositionText(composition); - EXPECT_STR_EQ("1234567890", model.text()); + EXPECT_STR_EQ("1234567890", model.GetText()); EXPECT_TRUE(model.SetText(ASCIIToUTF16("1234567890"))); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - model.MoveCursorToEnd(false); + model.MoveCursorRight(gfx::LINE_BREAK, false); model.SetCompositionText(composition); - EXPECT_STR_EQ("1234567890678", model.text()); + EXPECT_STR_EQ("1234567890678", model.GetText()); model.InsertText(UTF8ToUTF16("-")); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("1234567890-", model.text()); + EXPECT_STR_EQ("1234567890-", model.GetText()); EXPECT_FALSE(model.HasCompositionText()); EXPECT_FALSE(model.HasSelection()); - model.MoveCursorLeft(true); + model.MoveCursorLeft(gfx::CHARACTER_BREAK, true); EXPECT_STR_EQ("-", model.GetSelectedText()); model.SetCompositionText(composition); - EXPECT_STR_EQ("1234567890678", model.text()); + EXPECT_STR_EQ("1234567890678", model.GetText()); model.ReplaceText(UTF8ToUTF16("-")); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("1234567890-", model.text()); + EXPECT_STR_EQ("1234567890-", model.GetText()); EXPECT_FALSE(model.HasCompositionText()); EXPECT_FALSE(model.HasSelection()); @@ -583,82 +543,82 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) { model.Append(UTF8ToUTF16("-")); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("1234567890-678-", model.text()); + EXPECT_STR_EQ("1234567890-678-", model.GetText()); model.SetCompositionText(composition); model.Delete(); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("1234567890-678-", model.text()); + EXPECT_STR_EQ("1234567890-678-", model.GetText()); model.SetCompositionText(composition); model.Backspace(); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("1234567890-678-", model.text()); + EXPECT_STR_EQ("1234567890-678-", model.GetText()); model.SetText(string16()); model.SetCompositionText(composition); - model.MoveCursorLeft(false); + model.MoveCursorLeft(gfx::CHARACTER_BREAK, false); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("678", model.text()); - EXPECT_EQ(2U, model.cursor_pos()); + EXPECT_STR_EQ("678", model.GetText()); + EXPECT_EQ(2U, model.GetCursorPosition()); model.SetCompositionText(composition); - model.MoveCursorRight(false); + model.MoveCursorRight(gfx::CHARACTER_BREAK, false); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("676788", model.text()); - EXPECT_EQ(6U, model.cursor_pos()); + EXPECT_STR_EQ("676788", model.GetText()); + EXPECT_EQ(6U, model.GetCursorPosition()); model.SetCompositionText(composition); - model.MoveCursorToPreviousWord(false); + model.MoveCursorLeft(gfx::WORD_BREAK, false); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("676788678", model.text()); + EXPECT_STR_EQ("676788678", model.GetText()); model.SetText(string16()); model.SetCompositionText(composition); - model.MoveCursorToNextWord(false); + model.MoveCursorRight(gfx::WORD_BREAK, false); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; model.SetCompositionText(composition); - model.MoveCursorToHome(true); + model.MoveCursorLeft(gfx::LINE_BREAK, true); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("678678", model.text()); + EXPECT_STR_EQ("678678", model.GetText()); model.SetCompositionText(composition); - model.MoveCursorToEnd(false); + model.MoveCursorRight(gfx::LINE_BREAK, false); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("678", model.text()); + EXPECT_STR_EQ("678", model.GetText()); model.SetCompositionText(composition); model.MoveCursorTo(0, true); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("678678", model.text()); + EXPECT_STR_EQ("678678", model.GetText()); model.SetCompositionText(composition); model.SelectRange(ui::Range(0, 3)); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("678", model.text()); + EXPECT_STR_EQ("678", model.GetText()); model.SetCompositionText(composition); model.SelectAll(); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("678", model.text()); + EXPECT_STR_EQ("678", model.GetText()); model.SetCompositionText(composition); model.SelectWord(); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("678", model.text()); + EXPECT_STR_EQ("678", model.GetText()); model.SetCompositionText(composition); model.ClearSelection(); @@ -675,157 +635,157 @@ TEST_F(TextfieldViewsModelTest, UndoRedo_BasicTest) { model.InsertChar('a'); EXPECT_FALSE(model.Redo()); // nothing to redo EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.text()); + EXPECT_STR_EQ("", model.GetText()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("a", model.text()); + EXPECT_STR_EQ("a", model.GetText()); // Continuous inserts are treated as one edit. model.InsertChar('b'); model.InsertChar('c'); - EXPECT_STR_EQ("abc", model.text()); + EXPECT_STR_EQ("abc", model.GetText()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("a", model.text()); - EXPECT_EQ(1U, model.cursor_pos()); + EXPECT_STR_EQ("a", model.GetText()); + EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.text()); - EXPECT_EQ(0U, model.cursor_pos()); + EXPECT_STR_EQ("", model.GetText()); + EXPECT_EQ(0U, model.GetCursorPosition()); // Undoing further shouldn't change the text. EXPECT_FALSE(model.Undo()); - EXPECT_STR_EQ("", model.text()); + EXPECT_STR_EQ("", model.GetText()); EXPECT_FALSE(model.Undo()); - EXPECT_STR_EQ("", model.text()); - EXPECT_EQ(0U, model.cursor_pos()); + EXPECT_STR_EQ("", model.GetText()); + EXPECT_EQ(0U, model.GetCursorPosition()); // Redoing to the latest text. EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("a", model.text()); - EXPECT_EQ(1U, model.cursor_pos()); + EXPECT_STR_EQ("a", model.GetText()); + EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("abc", model.text()); - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_STR_EQ("abc", model.GetText()); + EXPECT_EQ(3U, model.GetCursorPosition()); // Backspace =============================== EXPECT_TRUE(model.Backspace()); - EXPECT_STR_EQ("ab", model.text()); + EXPECT_STR_EQ("ab", model.GetText()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("abc", model.text()); - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_STR_EQ("abc", model.GetText()); + EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ab", model.text()); - EXPECT_EQ(2U, model.cursor_pos()); + EXPECT_STR_EQ("ab", model.GetText()); + EXPECT_EQ(2U, model.GetCursorPosition()); // Continous backspaces are treated as one edit. EXPECT_TRUE(model.Backspace()); EXPECT_TRUE(model.Backspace()); - EXPECT_STR_EQ("", model.text()); + EXPECT_STR_EQ("", model.GetText()); // Extra backspace shouldn't affect the history. EXPECT_FALSE(model.Backspace()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ab", model.text()); - EXPECT_EQ(2U, model.cursor_pos()); + EXPECT_STR_EQ("ab", model.GetText()); + EXPECT_EQ(2U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("abc", model.text()); - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_STR_EQ("abc", model.GetText()); + EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("a", model.text()); - EXPECT_EQ(1U, model.cursor_pos()); + EXPECT_STR_EQ("a", model.GetText()); + EXPECT_EQ(1U, model.GetCursorPosition()); // Clear history model.ClearEditHistory(); EXPECT_FALSE(model.Undo()); EXPECT_FALSE(model.Redo()); - EXPECT_STR_EQ("a", model.text()); - EXPECT_EQ(1U, model.cursor_pos()); + EXPECT_STR_EQ("a", model.GetText()); + EXPECT_EQ(1U, model.GetCursorPosition()); // Delete =============================== model.SetText(ASCIIToUTF16("ABCDE")); model.ClearEditHistory(); model.MoveCursorTo(2, false); EXPECT_TRUE(model.Delete()); - EXPECT_STR_EQ("ABDE", model.text()); - model.MoveCursorToHome(false); + EXPECT_STR_EQ("ABDE", model.GetText()); + model.MoveCursorLeft(gfx::LINE_BREAK, false); EXPECT_TRUE(model.Delete()); - EXPECT_STR_EQ("BDE", model.text()); + EXPECT_STR_EQ("BDE", model.GetText()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABDE", model.text()); - EXPECT_EQ(0U, model.cursor_pos()); + EXPECT_STR_EQ("ABDE", model.GetText()); + EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.text()); - EXPECT_EQ(2U, model.cursor_pos()); + EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_EQ(2U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABDE", model.text()); - EXPECT_EQ(2U, model.cursor_pos()); + EXPECT_STR_EQ("ABDE", model.GetText()); + EXPECT_EQ(2U, model.GetCursorPosition()); // Continous deletes are treated as one edit. EXPECT_TRUE(model.Delete()); EXPECT_TRUE(model.Delete()); - EXPECT_STR_EQ("AB", model.text()); + EXPECT_STR_EQ("AB", model.GetText()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABDE", model.text()); - EXPECT_EQ(2U, model.cursor_pos()); + EXPECT_STR_EQ("ABDE", model.GetText()); + EXPECT_EQ(2U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("AB", model.text()); - EXPECT_EQ(2U, model.cursor_pos()); + EXPECT_STR_EQ("AB", model.GetText()); + EXPECT_EQ(2U, model.GetCursorPosition()); } TEST_F(TextfieldViewsModelTest, UndoRedo_SetText) { // This is to test the undo/redo behavior of omnibox. TextfieldViewsModel model(NULL); model.InsertChar('w'); - EXPECT_STR_EQ("w", model.text()); - EXPECT_EQ(1U, model.cursor_pos()); + EXPECT_STR_EQ("w", model.GetText()); + EXPECT_EQ(1U, model.GetCursorPosition()); model.SetText(ASCIIToUTF16("www.google.com")); - EXPECT_EQ(1U, model.cursor_pos()); - EXPECT_STR_EQ("www.google.com", model.text()); + EXPECT_EQ(1U, model.GetCursorPosition()); + EXPECT_STR_EQ("www.google.com", model.GetText()); model.SelectRange(ui::Range(14, 1)); model.InsertChar('w'); - EXPECT_STR_EQ("ww", model.text()); + EXPECT_STR_EQ("ww", model.GetText()); model.SetText(ASCIIToUTF16("www.google.com")); model.SelectRange(ui::Range(14, 2)); model.InsertChar('w'); - EXPECT_STR_EQ("www", model.text()); + EXPECT_STR_EQ("www", model.GetText()); model.SetText(ASCIIToUTF16("www.google.com")); model.SelectRange(ui::Range(14, 3)); model.InsertChar('.'); - EXPECT_STR_EQ("www.", model.text()); + EXPECT_STR_EQ("www.", model.GetText()); model.SetText(ASCIIToUTF16("www.google.com")); model.SelectRange(ui::Range(14, 4)); model.InsertChar('y'); - EXPECT_STR_EQ("www.y", model.text()); + EXPECT_STR_EQ("www.y", model.GetText()); model.SetText(ASCIIToUTF16("www.youtube.com")); - EXPECT_STR_EQ("www.youtube.com", model.text()); - EXPECT_EQ(5U, model.cursor_pos()); + EXPECT_STR_EQ("www.youtube.com", model.GetText()); + EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("www.google.com", model.text()); - EXPECT_EQ(4U, model.cursor_pos()); + EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_EQ(4U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("www.google.com", model.text()); - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("www.google.com", model.text()); - EXPECT_EQ(2U, model.cursor_pos()); + EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_EQ(2U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("www.google.com", model.text()); - EXPECT_EQ(1U, model.cursor_pos()); + EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.text()); - EXPECT_EQ(0U, model.cursor_pos()); + EXPECT_STR_EQ("", model.GetText()); + EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_FALSE(model.Undo()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("www.google.com", model.text()); - EXPECT_EQ(1U, model.cursor_pos()); + EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("www.google.com", model.text()); - EXPECT_EQ(2U, model.cursor_pos()); + EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_EQ(2U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("www.google.com", model.text()); - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("www.google.com", model.text()); - EXPECT_EQ(4U, model.cursor_pos()); + EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_EQ(4U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("www.youtube.com", model.text()); - EXPECT_EQ(5U, model.cursor_pos()); + EXPECT_STR_EQ("www.youtube.com", model.GetText()); + EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_FALSE(model.Redo()); } @@ -837,153 +797,153 @@ TEST_F(TextfieldViewsModelTest, UndoRedo_CutCopyPasteTest) { model.MoveCursorTo(1, false); model.MoveCursorTo(3, true); model.Cut(); - EXPECT_STR_EQ("ADE", model.text()); - EXPECT_EQ(1U, model.cursor_pos()); + EXPECT_STR_EQ("ADE", model.GetText()); + EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.text()); - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.text()); - EXPECT_EQ(0U, model.cursor_pos()); + EXPECT_STR_EQ("", model.GetText()); + EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_FALSE(model.Undo()); // no more undo - EXPECT_STR_EQ("", model.text()); + EXPECT_STR_EQ("", model.GetText()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCDE", model.text()); - EXPECT_EQ(0U, model.cursor_pos()); + EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ADE", model.text()); - EXPECT_EQ(1U, model.cursor_pos()); + EXPECT_STR_EQ("ADE", model.GetText()); + EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_FALSE(model.Redo()); // no more redo - EXPECT_STR_EQ("ADE", model.text()); + EXPECT_STR_EQ("ADE", model.GetText()); model.Paste(); model.Paste(); model.Paste(); - EXPECT_STR_EQ("ABCBCBCDE", model.text()); - EXPECT_EQ(7U, model.cursor_pos()); + EXPECT_STR_EQ("ABCBCBCDE", model.GetText()); + EXPECT_EQ(7U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCBCDE", model.text()); - EXPECT_EQ(5U, model.cursor_pos()); + EXPECT_STR_EQ("ABCBCDE", model.GetText()); + EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.text()); - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ADE", model.text()); - EXPECT_EQ(1U, model.cursor_pos()); + EXPECT_STR_EQ("ADE", model.GetText()); + EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.text()); - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.text()); - EXPECT_EQ(0U, model.cursor_pos()); + EXPECT_STR_EQ("", model.GetText()); + EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_FALSE(model.Undo()); - EXPECT_STR_EQ("", model.text()); + EXPECT_STR_EQ("", model.GetText()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCDE", model.text()); // Redoing SetText - EXPECT_EQ(0U, model.cursor_pos()); + EXPECT_STR_EQ("ABCDE", model.GetText()); // Redoing SetText + EXPECT_EQ(0U, model.GetCursorPosition()); // Redo EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ADE", model.text()); - EXPECT_EQ(1U, model.cursor_pos()); + EXPECT_STR_EQ("ADE", model.GetText()); + EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCDE", model.text()); - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCBCDE", model.text()); - EXPECT_EQ(5U, model.cursor_pos()); + EXPECT_STR_EQ("ABCBCDE", model.GetText()); + EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCBCBCDE", model.text()); - EXPECT_EQ(7U, model.cursor_pos()); + EXPECT_STR_EQ("ABCBCBCDE", model.GetText()); + EXPECT_EQ(7U, model.GetCursorPosition()); EXPECT_FALSE(model.Redo()); // with SelectRange model.SelectRange(ui::Range(1, 3)); EXPECT_TRUE(model.Cut()); - EXPECT_STR_EQ("ABCBCDE", model.text()); - EXPECT_EQ(1U, model.cursor_pos()); + EXPECT_STR_EQ("ABCBCDE", model.GetText()); + EXPECT_EQ(1U, model.GetCursorPosition()); model.SelectRange(ui::Range(1, 1)); EXPECT_FALSE(model.Cut()); - model.MoveCursorToEnd(false); + model.MoveCursorRight(gfx::LINE_BREAK, false); EXPECT_TRUE(model.Paste()); - EXPECT_STR_EQ("ABCBCDEBC", model.text()); - EXPECT_EQ(9U, model.cursor_pos()); + EXPECT_STR_EQ("ABCBCDEBC", model.GetText()); + EXPECT_EQ(9U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCBCDE", model.text()); - EXPECT_EQ(7U, model.cursor_pos()); + EXPECT_STR_EQ("ABCBCDE", model.GetText()); + EXPECT_EQ(7U, model.GetCursorPosition()); // empty cut shouldn't create an edit. EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCBCBCDE", model.text()); - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_STR_EQ("ABCBCBCDE", model.GetText()); + EXPECT_EQ(3U, model.GetCursorPosition()); // Copy ResetModel(&model); model.SetText(ASCIIToUTF16("12345")); - EXPECT_STR_EQ("12345", model.text()); - EXPECT_EQ(0U, model.cursor_pos()); + EXPECT_STR_EQ("12345", model.GetText()); + EXPECT_EQ(0U, model.GetCursorPosition()); model.MoveCursorTo(1, false); model.MoveCursorTo(3, true); model.Copy(); // Copy "23" - EXPECT_STR_EQ("12345", model.text()); - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_STR_EQ("12345", model.GetText()); + EXPECT_EQ(3U, model.GetCursorPosition()); model.Paste(); // Paste "23" into "23". - EXPECT_STR_EQ("12345", model.text()); - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_STR_EQ("12345", model.GetText()); + EXPECT_EQ(3U, model.GetCursorPosition()); model.Paste(); - EXPECT_STR_EQ("1232345", model.text()); - EXPECT_EQ(5U, model.cursor_pos()); + EXPECT_STR_EQ("1232345", model.GetText()); + EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("12345", model.text()); - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_STR_EQ("12345", model.GetText()); + EXPECT_EQ(3U, model.GetCursorPosition()); // TODO(oshima): We need to change the return type from bool to enum. EXPECT_FALSE(model.Undo()); // No text change. - EXPECT_STR_EQ("12345", model.text()); - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_STR_EQ("12345", model.GetText()); + EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.text()); + EXPECT_STR_EQ("", model.GetText()); EXPECT_FALSE(model.Undo()); // Redo EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("12345", model.text()); - EXPECT_EQ(0U, model.cursor_pos()); + EXPECT_STR_EQ("12345", model.GetText()); + EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("12345", model.text()); // For 1st paste - EXPECT_EQ(3U, model.cursor_pos()); + EXPECT_STR_EQ("12345", model.GetText()); // For 1st paste + EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("1232345", model.text()); - EXPECT_EQ(5U, model.cursor_pos()); + EXPECT_STR_EQ("1232345", model.GetText()); + EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_FALSE(model.Redo()); - EXPECT_STR_EQ("1232345", model.text()); + EXPECT_STR_EQ("1232345", model.GetText()); - // with SelectRange + // Test using SelectRange model.SelectRange(ui::Range(1, 3)); model.Copy(); - EXPECT_STR_EQ("1232345", model.text()); - model.MoveCursorToEnd(false); + EXPECT_STR_EQ("1232345", model.GetText()); + model.MoveCursorRight(gfx::LINE_BREAK, false); EXPECT_TRUE(model.Paste()); - EXPECT_STR_EQ("123234523", model.text()); - EXPECT_EQ(9U, model.cursor_pos()); + EXPECT_STR_EQ("123234523", model.GetText()); + EXPECT_EQ(9U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("1232345", model.text()); - EXPECT_EQ(7U, model.cursor_pos()); + EXPECT_STR_EQ("1232345", model.GetText()); + EXPECT_EQ(7U, model.GetCursorPosition()); } TEST_F(TextfieldViewsModelTest, UndoRedo_CursorTest) { TextfieldViewsModel model(NULL); model.InsertChar('a'); - model.MoveCursorLeft(false); - model.MoveCursorRight(false); + model.MoveCursorLeft(gfx::CHARACTER_BREAK, false); + model.MoveCursorRight(gfx::CHARACTER_BREAK, false); model.InsertChar('b'); - // Moving cursor shoudln't create a new edit. - EXPECT_STR_EQ("ab", model.text()); + // Moving the cursor shouldn't create a new edit. + EXPECT_STR_EQ("ab", model.GetText()); EXPECT_FALSE(model.Redo()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.text()); + EXPECT_STR_EQ("", model.GetText()); EXPECT_FALSE(model.Undo()); - EXPECT_STR_EQ("", model.text()); + EXPECT_STR_EQ("", model.GetText()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ab", model.text()); - EXPECT_EQ(2U, model.cursor_pos()); + EXPECT_STR_EQ("ab", model.GetText()); + EXPECT_EQ(2U, model.GetCursorPosition()); EXPECT_FALSE(model.Redo()); } @@ -995,21 +955,21 @@ void RunInsertReplaceTest(TextfieldViewsModel& model) { model.InsertChar('1'); model.InsertChar('2'); model.InsertChar('3'); - EXPECT_STR_EQ("a123d", model.text()); - EXPECT_EQ(4U, model.cursor_pos()); + EXPECT_STR_EQ("a123d", model.GetText()); + EXPECT_EQ(4U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("abcd", model.text()); - EXPECT_EQ(reverse ? 1U : 3U, model.cursor_pos()); + EXPECT_STR_EQ("abcd", model.GetText()); + EXPECT_EQ(reverse ? 1U : 3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.text()); - EXPECT_EQ(0U, model.cursor_pos()); + EXPECT_STR_EQ("", model.GetText()); + EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_FALSE(model.Undo()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("abcd", model.text()); - EXPECT_EQ(0U, model.cursor_pos()); // By SetText + EXPECT_STR_EQ("abcd", model.GetText()); + EXPECT_EQ(0U, model.GetCursorPosition()); // By SetText EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("a123d", model.text()); - EXPECT_EQ(4U, model.cursor_pos()); + EXPECT_STR_EQ("a123d", model.GetText()); + EXPECT_EQ(4U, model.GetCursorPosition()); EXPECT_FALSE(model.Redo()); } @@ -1022,21 +982,21 @@ void RunOverwriteReplaceTest(TextfieldViewsModel& model) { model.ReplaceChar('2'); model.ReplaceChar('3'); model.ReplaceChar('4'); - EXPECT_STR_EQ("a1234", model.text()); - EXPECT_EQ(5U, model.cursor_pos()); + EXPECT_STR_EQ("a1234", model.GetText()); + EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("abcd", model.text()); - EXPECT_EQ(reverse ? 1U : 3U, model.cursor_pos()); + EXPECT_STR_EQ("abcd", model.GetText()); + EXPECT_EQ(reverse ? 1U : 3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.text()); - EXPECT_EQ(0U, model.cursor_pos()); + EXPECT_STR_EQ("", model.GetText()); + EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_FALSE(model.Undo()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("abcd", model.text()); - EXPECT_EQ(0U, model.cursor_pos()); + EXPECT_STR_EQ("abcd", model.GetText()); + EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("a1234", model.text()); - EXPECT_EQ(5U, model.cursor_pos()); + EXPECT_STR_EQ("a1234", model.GetText()); + EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_FALSE(model.Redo()); } @@ -1114,282 +1074,72 @@ TEST_F(TextfieldViewsModelTest, UndoRedo_CompositionText) { composition.selection = ui::Range(2, 3); model.SetText(ASCIIToUTF16("ABCDE")); - model.MoveCursorToEnd(false); + model.MoveCursorRight(gfx::LINE_BREAK, false); model.InsertChar('x'); - EXPECT_STR_EQ("ABCDEx", model.text()); + EXPECT_STR_EQ("ABCDEx", model.GetText()); EXPECT_TRUE(model.Undo()); // set composition should forget undone edit. model.SetCompositionText(composition); EXPECT_TRUE(model.HasCompositionText()); EXPECT_TRUE(model.HasSelection()); - EXPECT_STR_EQ("ABCDEabc", model.text()); + EXPECT_STR_EQ("ABCDEabc", model.GetText()); // Accepting composition model.ConfirmCompositionText(); - EXPECT_STR_EQ("ABCDEabc", model.text()); + EXPECT_STR_EQ("ABCDEabc", model.GetText()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.text()); + EXPECT_STR_EQ("ABCDE", model.GetText()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.text()); + EXPECT_STR_EQ("", model.GetText()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCDE", model.text()); + EXPECT_STR_EQ("ABCDE", model.GetText()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCDEabc", model.text()); + EXPECT_STR_EQ("ABCDEabc", model.GetText()); EXPECT_FALSE(model.Redo()); // Canceling composition - model.MoveCursorToHome(false); + model.MoveCursorLeft(gfx::LINE_BREAK, false); model.SetCompositionText(composition); - EXPECT_STR_EQ("abcABCDEabc", model.text()); + EXPECT_STR_EQ("abcABCDEabc", model.GetText()); model.CancelCompositionText(); - EXPECT_STR_EQ("ABCDEabc", model.text()); + EXPECT_STR_EQ("ABCDEabc", model.GetText()); EXPECT_FALSE(model.Redo()); - EXPECT_STR_EQ("ABCDEabc", model.text()); + EXPECT_STR_EQ("ABCDEabc", model.GetText()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.text()); + EXPECT_STR_EQ("ABCDE", model.GetText()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCDEabc", model.text()); + EXPECT_STR_EQ("ABCDEabc", model.GetText()); EXPECT_FALSE(model.Redo()); // SetText with the same text as the result. ResetModel(&model); model.SetText(ASCIIToUTF16("ABCDE")); - model.MoveCursorToEnd(false); + model.MoveCursorRight(gfx::LINE_BREAK, false); model.SetCompositionText(composition); - EXPECT_STR_EQ("ABCDEabc", model.text()); + EXPECT_STR_EQ("ABCDEabc", model.GetText()); model.SetText(ASCIIToUTF16("ABCDEabc")); - EXPECT_STR_EQ("ABCDEabc", model.text()); + EXPECT_STR_EQ("ABCDEabc", model.GetText()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.text()); + EXPECT_STR_EQ("ABCDE", model.GetText()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCDEabc", model.text()); + EXPECT_STR_EQ("ABCDEabc", model.GetText()); EXPECT_FALSE(model.Redo()); // SetText with the different text than the result should not // remember composition text. ResetModel(&model); model.SetText(ASCIIToUTF16("ABCDE")); - model.MoveCursorToEnd(false); + model.MoveCursorRight(gfx::LINE_BREAK, false); model.SetCompositionText(composition); - EXPECT_STR_EQ("ABCDEabc", model.text()); + EXPECT_STR_EQ("ABCDEabc", model.GetText()); model.SetText(ASCIIToUTF16("1234")); - EXPECT_STR_EQ("1234", model.text()); + EXPECT_STR_EQ("1234", model.GetText()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.text()); + EXPECT_STR_EQ("ABCDE", model.GetText()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("1234", model.text()); + EXPECT_STR_EQ("1234", model.GetText()); EXPECT_FALSE(model.Redo()); // TODO(oshima): We need MockInputMethod to test the behavior with IME. } -TEST_F(TextfieldViewsModelTest, TextStyleTest) { - const SkColor black = 0xFF000000; // black is default text color. - const SkColor white = 0xFFFFFFFF; - TextfieldViewsModel model(NULL); - TextStyle* color = model.CreateTextStyle(); - color->set_foreground(white); - TextStyle* underline = model.CreateTextStyle(); - underline->set_underline(true); - underline->set_foreground(white); - TextStyle* strike = model.CreateTextStyle(); - strike->set_strike(true); - strike->set_foreground(white); - - // Case 1: No overlaps - model.ApplyTextStyle(color, ui::Range(1, 3)); - model.ApplyTextStyle(underline, ui::Range(5, 6)); - - TextfieldViewsModel::TextFragments fragments; - model.GetFragments(&fragments); - // Styles with empty string simply returns an empty fragments. - EXPECT_EQ(0U, fragments.size()); - - // 1st style only. - model.SetText(ASCIIToUTF16("01234")); // SetText doesn't change styles. - model.GetFragments(&fragments); - EXPECT_EQ(3U, fragments.size()); - EXPECT_EQ(0U, fragments[0].range.start()); - EXPECT_EQ(1U, fragments[0].range.end()); - EXPECT_EQ(black, fragments[0].style->foreground()); - - EXPECT_EQ(1U, fragments[1].range.start()); - EXPECT_EQ(3U, fragments[1].range.end()); - EXPECT_EQ(color, fragments[1].style); - - EXPECT_EQ(3U, fragments[2].range.start()); - EXPECT_EQ(5U, fragments[2].range.end()); - EXPECT_EQ(black, fragments[2].style->foreground()); - - // Clear styles - model.ClearAllTextStyles(); - model.GetFragments(&fragments); - EXPECT_EQ(1U, fragments.size()); - EXPECT_EQ(0U, fragments[0].range.start()); - EXPECT_EQ(5U, fragments[0].range.end()); - EXPECT_EQ(black, fragments[0].style->foreground()); - - // Case 2: Overlaps on left and on right - model.ApplyTextStyle(color, ui::Range(1, 3)); - model.ApplyTextStyle(strike, ui::Range(6, 8)); - model.ApplyTextStyle(underline, ui::Range(2, 7)); - - // With short string - model.SetText(ASCIIToUTF16("0")); - model.GetFragments(&fragments); - EXPECT_EQ(1U, fragments.size()); - EXPECT_EQ(0U, fragments[0].range.start()); - EXPECT_EQ(1U, fragments[0].range.end()); - EXPECT_EQ(black, fragments[0].style->foreground()); - - // With mid-length string - model.SetText(ASCIIToUTF16("0123")); - model.GetFragments(&fragments); - EXPECT_EQ(3U, fragments.size()); - EXPECT_EQ(0U, fragments[0].range.start()); - EXPECT_EQ(1U, fragments[0].range.end()); - EXPECT_EQ(black, fragments[0].style->foreground()); - - EXPECT_EQ(1U, fragments[1].range.start()); - EXPECT_EQ(2U, fragments[1].range.end()); - EXPECT_EQ(color, fragments[1].style); - - EXPECT_EQ(2U, fragments[2].range.start()); - EXPECT_EQ(4U, fragments[2].range.end()); - EXPECT_EQ(underline, fragments[2].style); - - // With long (longer than styles) string - model.SetText(ASCIIToUTF16("0123456789")); - model.GetFragments(&fragments); - EXPECT_EQ(5U, fragments.size()); - EXPECT_EQ(0U, fragments[0].range.start()); - EXPECT_EQ(1U, fragments[0].range.end()); - EXPECT_EQ(black, fragments[0].style->foreground()); - - EXPECT_EQ(1U, fragments[1].range.start()); - EXPECT_EQ(2U, fragments[1].range.end()); - EXPECT_EQ(color, fragments[1].style); - - EXPECT_EQ(2U, fragments[2].range.start()); - EXPECT_EQ(7U, fragments[2].range.end()); - EXPECT_EQ(underline, fragments[2].style); - - EXPECT_EQ(7U, fragments[3].range.start()); - EXPECT_EQ(8U, fragments[3].range.end()); - EXPECT_EQ(strike, fragments[3].style); - - EXPECT_EQ(8U, fragments[4].range.start()); - EXPECT_EQ(10U, fragments[4].range.end()); - EXPECT_EQ(black, fragments[4].style->foreground()); - - model.ClearAllTextStyles(); - - // Case 3: The underline style splits the color style underneath. - model.ApplyTextStyle(color, ui::Range(0, 15)); - model.ApplyTextStyle(underline, ui::Range(5, 6)); - model.GetFragments(&fragments); - EXPECT_EQ(3U, fragments.size()); - EXPECT_EQ(0U, fragments[0].range.start()); - EXPECT_EQ(5U, fragments[0].range.end()); - EXPECT_EQ(color, fragments[0].style); - - EXPECT_EQ(5U, fragments[1].range.start()); - EXPECT_EQ(6U, fragments[1].range.end()); - EXPECT_EQ(underline, fragments[1].style); - - EXPECT_EQ(6U, fragments[2].range.start()); - EXPECT_EQ(10U, fragments[2].range.end()); - EXPECT_EQ(color, fragments[2].style); - - model.ClearAllTextStyles(); - - // Case 4: The underline style moves the color style underneath. - model.ApplyTextStyle(color, ui::Range(0, 15)); - model.ApplyTextStyle(underline, ui::Range(0, 6)); - model.GetFragments(&fragments); - EXPECT_EQ(2U, fragments.size()); - EXPECT_EQ(0U, fragments[0].range.start()); - EXPECT_EQ(6U, fragments[0].range.end()); - EXPECT_EQ(underline, fragments[0].style); - - EXPECT_EQ(6U, fragments[1].range.start()); - EXPECT_EQ(10U, fragments[1].range.end()); - EXPECT_EQ(color, fragments[1].style); - - model.ClearAllTextStyles(); - - model.ApplyTextStyle(color, ui::Range(0, 10)); - model.ApplyTextStyle(underline, ui::Range(6, 10)); - model.GetFragments(&fragments); - EXPECT_EQ(2U, fragments.size()); - EXPECT_EQ(0U, fragments[0].range.start()); - EXPECT_EQ(6U, fragments[0].range.end()); - EXPECT_EQ(color, fragments[0].style); - - EXPECT_EQ(6U, fragments[1].range.start()); - EXPECT_EQ(10U, fragments[1].range.end()); - EXPECT_EQ(underline, fragments[1].style); - - model.ClearAllTextStyles(); - // Case 5: The strike style hides the unerline style underneath. - model.ApplyTextStyle(color, ui::Range(0, 15)); - model.ApplyTextStyle(underline, ui::Range(0, 6)); - model.ApplyTextStyle(strike, ui::Range(4, 7)); - model.GetFragments(&fragments); - EXPECT_EQ(3U, fragments.size()); - EXPECT_EQ(0U, fragments[0].range.start()); - EXPECT_EQ(4U, fragments[0].range.end()); - EXPECT_EQ(underline, fragments[0].style); - - EXPECT_EQ(4U, fragments[1].range.start()); - EXPECT_EQ(7U, fragments[1].range.end()); - EXPECT_EQ(strike, fragments[1].style); - - EXPECT_EQ(7U, fragments[2].range.start()); - EXPECT_EQ(10U, fragments[2].range.end()); - EXPECT_EQ(color, fragments[2].style); - - // Case 6: Reversed range. - model.ClearAllTextStyles(); - model.ApplyTextStyle(color, ui::Range(3, 1)); - model.ApplyTextStyle(underline, ui::Range(6, 4)); - model.ApplyTextStyle(strike, ui::Range(5, 2)); - model.GetFragments(&fragments); - EXPECT_EQ(5U, fragments.size()); - - EXPECT_EQ(0U, fragments[0].range.start()); - EXPECT_EQ(1U, fragments[0].range.end()); - EXPECT_EQ(black, fragments[0].style->foreground()); - - EXPECT_EQ(1U, fragments[1].range.start()); - EXPECT_EQ(2U, fragments[1].range.end()); - EXPECT_EQ(color, fragments[1].style); - - EXPECT_EQ(2U, fragments[2].range.start()); - EXPECT_EQ(5U, fragments[2].range.end()); - EXPECT_EQ(strike, fragments[2].style); - - EXPECT_EQ(5U, fragments[3].range.start()); - EXPECT_EQ(6U, fragments[3].range.end()); - EXPECT_EQ(underline, fragments[3].style); - - EXPECT_EQ(6U, fragments[4].range.start()); - EXPECT_EQ(10U, fragments[4].range.end()); - EXPECT_EQ(black, fragments[4].style->foreground()); - - // Case 7: empty / invald range - model.ClearAllTextStyles(); - model.ApplyTextStyle(color, ui::Range(0, 0)); - model.ApplyTextStyle(underline, ui::Range(4, 4)); - ui::Range invalid = ui::Range(0, 2).Intersect(ui::Range(3, 4)); - ASSERT_FALSE(invalid.IsValid()); - - model.ApplyTextStyle(strike, invalid); - model.GetFragments(&fragments); - EXPECT_EQ(1U, fragments.size()); - - EXPECT_EQ(0U, fragments[0].range.start()); - EXPECT_EQ(10U, fragments[0].range.end()); - EXPECT_EQ(black, fragments[0].style->foreground()); -} - } // namespace views |