Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(477)

Unified Diff: views/controls/textfield/textfield_views_model.cc

Issue 7067015: An edit for SetText needs to be merged with previous edit for omnibox. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: " Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 89253a626c92dbd576861fa53aac976677f6d241..b1773ee9cd5ac44898b8d85227a52fe5d508aacb 100644
--- a/views/controls/textfield/textfield_views_model.cc
+++ b/views/controls/textfield/textfield_views_model.cc
@@ -41,28 +41,31 @@ class Edit {
// Revert the change made by this edit in |model|.
void Undo(TextfieldViewsModel* model) {
- size_t old_cursor = delete_backward_ ? old_text_end() : old_text_start_;
model->ModifyText(new_text_start_, new_text_end(),
old_text_, old_text_start_,
- old_cursor);
+ old_cursor_pos_);
}
// Apply the change of this edit to the |model|.
void Redo(TextfieldViewsModel* model) {
model->ModifyText(old_text_start_, old_text_end(),
new_text_, new_text_start_,
- new_text_end());
+ new_cursor_pos_);
}
- // Try to merge the edit into this edit. Returns true if merge was
+ // Try to merge the |edit| into this edit. Returns true if merge was
// successful, or false otherwise. Merged edit will be deleted after
// redo and should not be reused.
- bool Merge(Edit* edit) {
- return mergeable_ && edit->mergeable() && DoMerge(edit);
+ bool Merge(const Edit* edit) {
+ if (edit->merge_with_previous()) {
+ MergeSet(edit);
+ return true;
+ }
+ return mergeable() && edit->mergeable() && DoMerge(edit);
}
// Commits the edit and marks as un-mergeable.
- void Commit() { mergeable_ = false; }
+ void Commit() { merge_type_ = DO_NOT_MERGE; }
private:
friend class InsertEdit;
@@ -70,46 +73,79 @@ class Edit {
friend class DeleteEdit;
Edit(Type type,
- bool mergeable,
+ MergeType merge_type,
+ size_t old_cursor_pos,
string16 old_text,
size_t old_text_start,
bool delete_backward,
+ size_t new_cursor_pos,
string16 new_text,
size_t new_text_start)
: type_(type),
- mergeable_(mergeable),
+ merge_type_(merge_type),
+ old_cursor_pos_(old_cursor_pos),
old_text_(old_text),
old_text_start_(old_text_start),
delete_backward_(delete_backward),
+ new_cursor_pos_(new_cursor_pos),
new_text_(new_text),
new_text_start_(new_text_start) {
}
// A template method pattern that provides specific merge
// implementation for each type of edit.
- virtual bool DoMerge(Edit* edit) = 0;
+ virtual bool DoMerge(const Edit* edit) = 0;
- Type type() { return type_; }
+ Type type() const { return type_; }
// Can this edit be merged?
- bool mergeable() { return mergeable_; }
+ bool mergeable() const { return merge_type_ == MERGEABLE; }
+
+ // Does this edit should be merged with previous edit?
msw 2011/06/02 21:02:57 okay, the function is fine, but the comment needs
oshima 2011/06/03 00:39:24 Done.
+ bool merge_with_previous() const {
+ return merge_type_ == MERGE_WITH_PREVIOUS;
+ }
// Returns the end index of the |old_text_|.
- size_t old_text_end() { return old_text_start_ + old_text_.length(); }
+ size_t old_text_end() const { return old_text_start_ + old_text_.length(); }
// Returns the end index of the |new_text_|.
- size_t new_text_end() { return new_text_start_ + new_text_.length(); }
+ size_t new_text_end() const { return new_text_start_ + new_text_.length(); }
+
+ // Merge the Set edit into the current edit. This is a special case to
+ // handle an omnibox setting autocomplete string after new character is
+ // typed in.
+ void MergeSet(const Edit* edit) {
+ CHECK_EQ(REPLACE_EDIT, edit->type_);
+ CHECK_EQ(0U, edit->old_text_start_);
+ CHECK_EQ(0U, edit->new_text_start_);
+ string16 old_text = edit->old_text_;
+ old_text.erase(new_text_start_, new_text_.length());
+ old_text.insert(old_text_start_, old_text_);
+ // Set edit replaces entire text, so remember that.
msw 2011/06/02 21:02:57 I ran through an example just now and it makes sen
oshima 2011/06/03 00:39:24 Done.
+ old_text_ = old_text;
+ old_text_start_ = edit->old_text_start_;
+ delete_backward_ = false;
+
+ new_text_ = edit->new_text_;
+ new_text_start_ = edit->new_text_start_;
+ merge_type_ = DO_NOT_MERGE;
+ }
Type type_;
// True if the edit can be marged.
- bool mergeable_;
+ MergeType merge_type_;
+ // Old cursor position.
+ size_t old_cursor_pos_;
// Deleted text by this edit.
string16 old_text_;
// The index of |old_text_|.
size_t old_text_start_;
// True if the deletion is made backward.
bool delete_backward_;
+ // New cursor position.
+ size_t new_cursor_pos_;
// Added text.
string16 new_text_;
// The index of |new_text_|
@@ -121,45 +157,59 @@ class Edit {
class InsertEdit : public Edit {
public:
InsertEdit(bool mergeable, const string16& new_text, size_t at)
- : Edit(INSERT_EDIT, mergeable, string16(), at, false, new_text, at) {
+ : Edit(INSERT_EDIT,
+ mergeable ? MERGEABLE : DO_NOT_MERGE,
+ at /* old cursor */,
+ string16(),
+ at,
+ false /* N/A */,
+ at + new_text.length() /* new cursor */,
+ new_text,
+ at) {
}
// Edit implementation.
- virtual bool DoMerge(Edit* edit) OVERRIDE {
+ virtual bool DoMerge(const Edit* edit) OVERRIDE {
if (edit->type() != INSERT_EDIT || new_text_end() != edit->new_text_start_)
return false;
// If continuous edit, merge it.
// TODO(oshima): gtk splits edits between whitespace. Find out what
// we want to here and implement if necessary.
new_text_ += edit->new_text_;
+ new_cursor_pos_ = edit->new_cursor_pos_;
return true;
}
};
class ReplaceEdit : public Edit {
public:
- ReplaceEdit(bool mergeable,
+ ReplaceEdit(MergeType merge_type,
const string16& old_text,
+ size_t old_cursor_pos,
size_t old_text_start,
bool backward,
+ size_t new_cursor_pos,
const string16& new_text,
size_t new_text_start)
- : Edit(REPLACE_EDIT, mergeable,
+ : Edit(REPLACE_EDIT, merge_type,
+ old_cursor_pos,
old_text,
old_text_start,
backward,
+ new_cursor_pos,
new_text,
new_text_start) {
}
// Edit implementation.
- virtual bool DoMerge(Edit* edit) OVERRIDE {
+ virtual bool DoMerge(const Edit* edit) OVERRIDE {
if (edit->type() == DELETE_EDIT ||
new_text_end() != edit->old_text_start_ ||
edit->old_text_start_ != edit->new_text_start_)
return false;
old_text_ += edit->old_text_;
new_text_ += edit->new_text_;
+ new_cursor_pos_ = edit->new_cursor_pos_;
return true;
}
};
@@ -170,16 +220,19 @@ class DeleteEdit : public Edit {
const string16& text,
size_t text_start,
bool backward)
- : Edit(DELETE_EDIT, mergeable,
+ : Edit(DELETE_EDIT,
+ mergeable ? MERGEABLE : DO_NOT_MERGE,
+ (backward ? text_start + text.length() : text_start),
text,
text_start,
backward,
+ text_start,
string16(),
text_start) {
}
// Edit implementation.
- virtual bool DoMerge(Edit* edit) OVERRIDE {
+ virtual bool DoMerge(const Edit* edit) OVERRIDE {
if (edit->type() != DELETE_EDIT)
return false;
@@ -190,6 +243,7 @@ class DeleteEdit : public Edit {
return false;
old_text_start_ = edit->old_text_start_;
old_text_ = edit->old_text_ + old_text_;
+ new_cursor_pos_ = edit->new_cursor_pos_;
} else {
// delete can be merged only with delete at the same
// position.
@@ -313,6 +367,10 @@ using internal::Edit;
using internal::DeleteEdit;
using internal::InsertEdit;
using internal::ReplaceEdit;
+using internal::MergeType;
+using internal::DO_NOT_MERGE;
+using internal::MERGE_WITH_PREVIOUS;
+using internal::MERGEABLE;
/////////////////////////////////////////////////////////////////
// TextfieldViewsModel: public
@@ -395,11 +453,20 @@ bool TextfieldViewsModel::SetText(const string16& text) {
changed = true;
}
if (text_ != text) {
- if (changed) // no need to remember composition.
+ if (changed) // No need to remember composition.
Undo();
+ size_t old_cursor = cursor_pos_;
+ size_t new_cursor = old_cursor > text.length() ? text.length() : old_cursor;
SelectAll();
- InsertTextInternal(text, false);
- cursor_pos_ = 0;
+ // If there is a composition text, don't merge with previous edit.
+ // Otherwise, force merge the edits.
+ ExecuteAndRecordReplace(
+ changed ? DO_NOT_MERGE : MERGE_WITH_PREVIOUS,
+ old_cursor,
+ new_cursor,
+ text,
+ 0U);
+ cursor_pos_ = new_cursor;
}
ClearSelection();
return changed;
@@ -667,6 +734,7 @@ bool TextfieldViewsModel::Undo() {
CancelCompositionText();
string16 old = text_;
+ size_t old_cursor = cursor_pos_;
(*current_edit_)->Commit();
(*current_edit_)->Undo(this);
@@ -674,7 +742,7 @@ bool TextfieldViewsModel::Undo() {
current_edit_ = edit_history_.end();
else
current_edit_--;
- return old != text_;
+ return old != text_ || old_cursor != cursor_pos_;
}
bool TextfieldViewsModel::Redo() {
@@ -689,14 +757,21 @@ bool TextfieldViewsModel::Redo() {
else
current_edit_ ++;
string16 old = text_;
+ size_t old_cursor = cursor_pos_;
(*current_edit_)->Redo(this);
- return old != text_;
+ return old != text_ || old_cursor != cursor_pos_;
}
bool TextfieldViewsModel::Cut() {
if (!HasCompositionText() && HasSelection()) {
ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate
->GetClipboard()).WriteText(GetSelectedText());
+ // A trick to let undo/redo handle cursor correctly.
+ // Undoing CUT moves the cursor to the end of the change rather
+ // 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_);
DeleteSelection();
return true;
}
@@ -735,7 +810,11 @@ void TextfieldViewsModel::DeleteSelectionAndInsertTextAt(
const string16& text, size_t position) {
if (HasCompositionText())
CancelCompositionText();
- ExecuteAndRecordReplaceAt(text, position, false);
+ ExecuteAndRecordReplace(DO_NOT_MERGE,
+ cursor_pos_,
+ position + text.length(),
+ text,
+ position);
}
string16 TextfieldViewsModel::GetTextFromRange(const ui::Range& range) const {
@@ -879,7 +958,8 @@ void TextfieldViewsModel::InsertTextInternal(const string16& text,
CancelCompositionText();
ExecuteAndRecordInsert(text, mergeable);
} else if (HasSelection()) {
- ExecuteAndRecordReplace(text, mergeable);
+ ExecuteAndRecordReplaceSelection(mergeable ? MERGEABLE : DO_NOT_MERGE,
+ text);
} else {
ExecuteAndRecordInsert(text, mergeable);
}
@@ -911,8 +991,7 @@ void TextfieldViewsModel::ClearRedoHistory() {
}
EditHistory::iterator delete_start = current_edit_;
delete_start++;
- STLDeleteContainerPointers(delete_start,
- edit_history_.end());
+ STLDeleteContainerPointers(delete_start, edit_history_.end());
edit_history_.erase(delete_start, edit_history_.end());
}
@@ -922,32 +1001,40 @@ void TextfieldViewsModel::ExecuteAndRecordDelete(size_t from,
size_t old_text_start = std::min(from, to);
const string16 text = text_.substr(old_text_start,
std::abs(static_cast<long>(from - to)));
- Edit* edit = new DeleteEdit(mergeable,
- text,
- old_text_start,
- (from > to));
+ bool backward = from > to;
+ Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward);
bool delete_edit = AddOrMergeEditHistory(edit);
edit->Redo(this);
if (delete_edit)
delete edit;
}
-void TextfieldViewsModel::ExecuteAndRecordReplace(const string16& text,
- bool mergeable) {
- size_t at = std::min(cursor_pos_, selection_start_);
- ExecuteAndRecordReplaceAt(text, at, mergeable);
+void TextfieldViewsModel::ExecuteAndRecordReplaceSelection(
+ MergeType merge_type, const string16& new_text) {
+ size_t new_text_start = std::min(cursor_pos_, selection_start_);
+ size_t new_cursor_pos = new_text_start + new_text.length();
+ ExecuteAndRecordReplace(merge_type,
+ cursor_pos_,
+ new_cursor_pos,
+ new_text,
+ new_text_start);
}
-void TextfieldViewsModel::ExecuteAndRecordReplaceAt(const string16& text,
- size_t at,
- bool mergeable) {
- size_t text_start = std::min(cursor_pos_, selection_start_);
- Edit* edit = new ReplaceEdit(mergeable,
+void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type,
+ size_t old_cursor_pos,
+ 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_;
+ Edit* edit = new ReplaceEdit(merge_type,
GetSelectedText(),
- text_start,
- selection_start_ > cursor_pos_,
- text,
- at);
+ old_cursor_pos,
+ old_text_start,
+ backward,
+ new_cursor_pos,
+ new_text,
+ new_text_start);
bool delete_edit = AddOrMergeEditHistory(edit);
edit->Redo(this);
if (delete_edit)

Powered by Google App Engine
This is Rietveld 408576698