diff options
author | oshima@google.com <oshima@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-11 01:24:09 +0000 |
---|---|---|
committer | oshima@google.com <oshima@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-11 01:24:09 +0000 |
commit | 1cbb4c4a87c0e1e87defd6ab919217280d275b3f (patch) | |
tree | 6f8d1bc47b919807cae1876053e3050797ce1e6d /views/controls/textfield/textfield_views_model.h | |
parent | c9d88a05388dfc5702aa018fc06d800741a6a385 (diff) | |
download | chromium_src-1cbb4c4a87c0e1e87defd6ab919217280d275b3f.zip chromium_src-1cbb4c4a87c0e1e87defd6ab919217280d275b3f.tar.gz chromium_src-1cbb4c4a87c0e1e87defd6ab919217280d275b3f.tar.bz2 |
Undo Redo for Textfield Views
Added Edit and its subclass that represents a change that can be undone/redone.
Set TestViewsDelegates in examples_main.cc to make cut© work in views_examples.
I'll refactor this soon so that text change is always done by an edit object.
BUG=none
TEST=unit tests for undo/redo
Review URL: http://codereview.chromium.org/6937002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@84902 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views/controls/textfield/textfield_views_model.h')
-rw-r--r-- | views/controls/textfield/textfield_views_model.h | 102 |
1 files changed, 93 insertions, 9 deletions
diff --git a/views/controls/textfield/textfield_views_model.h b/views/controls/textfield/textfield_views_model.h index 260d493..55fd6e2 100644 --- a/views/controls/textfield/textfield_views_model.h +++ b/views/controls/textfield/textfield_views_model.h @@ -6,8 +6,10 @@ #define VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_VIEWS_MODEL_H_ #pragma once +#include <list> #include <vector> +#include "base/gtest_prod_util.h" #include "base/string16.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/ime/composition_text.h" @@ -23,6 +25,11 @@ class Range; namespace views { +namespace internal { +// Internal Edit class that keeps track of edits for undo/redo. +class Edit; +} // namespace internal + // A model that represents a text content for TextfieldViews. // It supports editing, selection and cursor manipulation. class TextfieldViewsModel { @@ -70,26 +77,34 @@ class TextfieldViewsModel { // Edit related methods. - // Sest the text. Returns true if the text has been modified. - // The current composition text will be confirmed first. + // Sest 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 + // code to set the same text multiple times without worrying about + // messing edit history. bool SetText(const string16& text); // Inserts given |text| at the current cursor position. // The current composition text will be cleared. - void InsertText(const string16& text); + void InsertText(const string16& text) { + InsertTextInternal(text, false); + } // Inserts a character at the current cursor position. void InsertChar(char16 c) { - InsertText(string16(&c, 1)); + InsertTextInternal(string16(&c, 1), true); } // Replaces characters at the current position with characters in given text. // The current composition text will be cleared. - void ReplaceText(const string16& text); + void ReplaceText(const string16& text) { + ReplaceTextInternal(text, false); + } // Replaces the char at the current position with given character. void ReplaceChar(char16 c) { - ReplaceText(string16(&c, 1)); + ReplaceTextInternal(string16(&c, 1), true); } // Appends the text. @@ -159,7 +174,10 @@ class TextfieldViewsModel { void GetSelectedRange(ui::Range* range) const; - // The current composition text will be confirmed. + // The current composition text will be confirmed. The + // selection starts with the range's start position, + // and ends with the range's end position, therefore + // the cursor position becomes the end position. void SelectRange(const ui::Range& range); // Selects all text. @@ -174,6 +192,18 @@ class TextfieldViewsModel { // The current composition text will be confirmed. void ClearSelection(); + // Returns true if there is an undoable edit. + bool CanUndo(); + + // Returns true if there is an redoable edit. + bool CanRedo(); + + // Undo edit. Returns true if undo changed the text. + bool Undo(); + + // Redo edit. Returns true if redo changed the text. + bool Redo(); + // Returns visible text. If the field is password, it returns the // sequence of "*". string16 GetVisibleText() const { @@ -196,8 +226,9 @@ class TextfieldViewsModel { bool HasSelection() const; // Deletes the selected text. This method shouldn't be called with - // composition text. - void DeleteSelection(); + // composition text. If |record_edit_history| is true, the deletion + // will be recorded so that it can be undone or redone. + void DeleteSelection(bool record_edit_history); // Retrieves the text content in a given range. string16 GetTextFromRange(const ui::Range& range) const; @@ -227,6 +258,16 @@ class TextfieldViewsModel { private: friend class NativeTextfieldViews; + friend class NativeTextfieldViewsTest; + friend class TextfieldViewsModelTest; + friend class UndoRedo_BasicTest; + friend class UndoRedo_CutCopyPasteTest; + friend class UndoRedo_ReplaceTest; + friend class internal::Edit; + + FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_BasicTest); + FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_CutCopyPasteTest); + FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_ReplaceTest); // Returns the visible text given |start| and |end|. string16 GetVisibleText(size_t start, size_t end) const; @@ -238,6 +279,29 @@ class TextfieldViewsModel { // 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); + + // Replace the current text with the given |text|. |mergeable| + // indicates if this replace operation can be merged to previous + // edit in the edit history. + void ReplaceTextInternal(const string16& text, bool mergeable); + + // Clears all edit history. + void ClearEditHistory(); + + // Clears redo history. + void ClearRedoHistory(); + + // Records edit operations. + void RecordDelete(size_t cursor_pos, int size, bool mergeable); + void RecordReplace(const string16& text, bool mergeable); + void RecordInsert(const string16& text, bool mergeable); + + // Adds edit into edit history. + void AddEditHistory(internal::Edit* edit); + // Pointer to a TextfieldViewsModel::Delegate instance, should be provided by // the View object. Delegate* delegate_; @@ -261,6 +325,26 @@ class TextfieldViewsModel { // True if the text is the password. bool is_password_; + typedef std::list<internal::Edit*> EditHistory; + EditHistory edit_history_; + + // An iterator that points to the current edit that can be undone. + // This iterator moves from the |end()|, meaining no edit to undo, + // to the last element (one before |end()|), meaning no edit to redo. + // There is no edit to undo (== end()) when: + // 1) in initial state. (nothing to undo) + // 2) very 1st edit is undone. + // 3) all edit history is removed. + // There is no edit to redo (== last element or no element) when: + // 1) in initial state. (nothing to redo) + // 2) new edit is added. (redo history is cleared) + // 3) redone all undone edits. + EditHistory::iterator current_edit_; + + // A guard variable to prevent edit history from being updated while + // performing undo/redo. + bool update_history_; + DISALLOW_COPY_AND_ASSIGN(TextfieldViewsModel); }; |