Chromium Code Reviews| Index: views/controls/textfield/textfield_views_model.cc |
| diff --git a/views/controls/textfield/textfield_views_model.cc b/views/controls/textfield/textfield_views_model.cc |
| index 1605546307bf7ef519883e9f5765c571f779d9e3..15aa039b3a1acc5883176d00533df6a1324c59cc 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,19 @@ 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; |
| - } |
| + delete render_text_; |
|
oshima
2011/07/23 09:51:12
why not scoped_ptr?
msw
2011/07/25 05:09:54
Done.
|
| } |
| -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 +297,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 = render_text_->GetCursor(); |
| 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 +311,7 @@ bool TextfieldViewsModel::SetText(const string16& text) { |
| new_cursor, |
| text, |
| 0U); |
| - cursor_pos_ = new_cursor; |
| + render_text_->SetCursor(new_cursor); |
| } |
| ClearSelection(); |
| return changed; |
| @@ -478,10 +320,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 = render_text_->GetCursor(); |
| + MoveCursorRight(gfx::LINE_BREAK, false); |
| InsertText(text); |
| - cursor_pos_ = save; |
| + render_text_->SetCursor(save); |
| ClearSelection(); |
| } |
| @@ -495,8 +337,9 @@ bool TextfieldViewsModel::Delete() { |
| DeleteSelection(); |
| return true; |
| } |
| - if (text_.length() > cursor_pos_) { |
| - ExecuteAndRecordDelete(cursor_pos_, cursor_pos_ + 1, true); |
| + if (GetText().length() > render_text_->GetCursor()) { |
| + size_t cursor_position = render_text_->GetCursor(); |
| + ExecuteAndRecordDelete(cursor_position, cursor_position + 1, true); |
| return true; |
| } |
| return false; |
| @@ -512,194 +355,79 @@ bool TextfieldViewsModel::Backspace() { |
| DeleteSelection(); |
| return true; |
| } |
| - if (cursor_pos_ > 0) { |
| - ExecuteAndRecordDelete(cursor_pos_, cursor_pos_ - 1, true); |
| + if (render_text_->GetCursor() > 0) { |
| + size_t cursor_position = render_text_->GetCursor(); |
| + 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_->GetCursor(); |
| } |
| -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(); |
| + return render_text_->MoveCursorTo(pos, select); |
| } |
| -void TextfieldViewsModel::MoveCursorToHome(bool select) { |
| - MoveCursorTo(0, select); |
| -} |
| - |
| -void TextfieldViewsModel::MoveCursorToEnd(bool select) { |
| - MoveCursorTo(text_.length(), 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 +451,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 = render_text_->GetCursor(); |
| (*current_edit_)->Commit(); |
| (*current_edit_)->Undo(this); |
| @@ -732,7 +460,7 @@ bool TextfieldViewsModel::Undo() { |
| current_edit_ = edit_history_.end(); |
| else |
| current_edit_--; |
| - return old != text_ || old_cursor != cursor_pos_; |
| + return old != GetText() || old_cursor != render_text_->GetCursor(); |
| } |
| bool TextfieldViewsModel::Redo() { |
| @@ -746,10 +474,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 = render_text_->GetCursor(); |
| (*current_edit_)->Redo(this); |
| - return old != text_ || old_cursor != cursor_pos_; |
| + return old != GetText() || old_cursor != render_text_->GetCursor(); |
| +} |
| + |
| +string16 TextfieldViewsModel::GetVisibleText() const { |
| + return GetVisibleText(0U, GetText().length()); |
| } |
| bool TextfieldViewsModel::Cut() { |
| @@ -761,7 +493,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())); |
|
oshima
2011/07/23 09:51:12
doesn't range have a method to swap position?
msw
2011/07/25 05:09:54
Not that I see.
|
| DeleteSelection(); |
| return true; |
| } |
| @@ -787,13 +520,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 +535,24 @@ void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( |
| if (HasCompositionText()) |
| CancelCompositionText(); |
| ExecuteAndRecordReplace(DO_NOT_MERGE, |
| - cursor_pos_, |
| + render_text_->GetCursor(), |
| 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 +561,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 = render_text_->GetCursor(); |
| + 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_->SetCursor(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_->SetCursor(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_->SetCursor(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 +619,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, |
| @@ -959,8 +639,10 @@ void TextfieldViewsModel::ReplaceTextInternal(const string16& text, |
| bool mergeable) { |
| if (HasCompositionText()) |
| CancelCompositionText(); |
| - else if (!HasSelection()) |
| - SelectRange(ui::Range(cursor_pos_ + text.length(), cursor_pos_)); |
| + else if (!HasSelection()) { |
|
oshima
2011/07/23 09:51:12
you need {} for the 1st if, i believe. check style
msw
2011/07/25 05:09:54
Done.
|
| + size_t cursor = render_text_->GetCursor(); |
| + render_text_->SetSelection(ui::Range(cursor + text.length(), cursor)); |
| + } |
| // Edit history is recorded in InsertText. |
| InsertTextInternal(text, mergeable); |
| } |
| @@ -989,7 +671,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 +683,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_, |
| + render_text_->GetCursor(), |
| new_cursor_pos, |
| new_text, |
| new_text_start); |
| @@ -1015,8 +697,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 +715,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, render_text_->GetCursor()); |
| bool delete_edit = AddOrMergeEditHistory(edit); |
| edit->Redo(this); |
| if (delete_edit) |
| @@ -1067,21 +749,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_->SetCursor(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 |