summaryrefslogtreecommitdiffstats
path: root/views/controls/textfield/textfield_views_model.h
diff options
context:
space:
mode:
authoroshima@google.com <oshima@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-11 01:24:09 +0000
committeroshima@google.com <oshima@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-11 01:24:09 +0000
commit1cbb4c4a87c0e1e87defd6ab919217280d275b3f (patch)
tree6f8d1bc47b919807cae1876053e3050797ce1e6d /views/controls/textfield/textfield_views_model.h
parentc9d88a05388dfc5702aa018fc06d800741a6a385 (diff)
downloadchromium_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&copy 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.h102
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);
};